Features: 1) Add currency symbol display in dashboard; 2) Add type hints to dashboard_callback and related functions;
Fixes: 1) Fix type annotations for get_revenue, get_returns, get_total_processed_orders; 2) Add missing import for Context; Extra: 1) Add type hint for currency_symbol; 2) Add type ignore comments for image_url; 3) Update UNFOLD config to use dict[str, Any]; 4) Add missing import for typing.Any; 5) Refactor template to conditionally display currency symbol.
This commit is contained in:
parent
fa165244b8
commit
00a172d463
4 changed files with 37 additions and 23 deletions
|
|
@ -27,7 +27,7 @@
|
||||||
{% trans "Revenue (gross, 30d)" %}
|
{% trans "Revenue (gross, 30d)" %}
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
{% component "unfold/components/title.html" %}
|
{% component "unfold/components/title.html" %}
|
||||||
{{ revenue_gross_30|default:0 }}
|
{% if currency_symbol %}{{ currency_symbol }}{% endif %}{{ revenue_gross_30|default:0 }}
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
{% trans "Revenue (net, 30d)" %}
|
{% trans "Revenue (net, 30d)" %}
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
{% component "unfold/components/title.html" %}
|
{% component "unfold/components/title.html" %}
|
||||||
{{ revenue_net_30|default:0 }}
|
{% if currency_symbol %}{{ currency_symbol }}{% endif %}{{ revenue_net_30|default:0 }}
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
{% trans "Returns (30d)" %}
|
{% trans "Returns (30d)" %}
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
{% component "unfold/components/title.html" %}
|
{% component "unfold/components/title.html" %}
|
||||||
{{ returns_30|default:0 }}
|
{% if currency_symbol %}{{ currency_symbol }}{% endif %}{{ returns_30|default:0 }}
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
{% endcomponent %}
|
{% endcomponent %}
|
||||||
|
|
||||||
|
|
@ -83,13 +83,13 @@
|
||||||
<span class="inline-block w-3 h-3 rounded-sm"
|
<span class="inline-block w-3 h-3 rounded-sm"
|
||||||
style="background:rgb(34,197,94)"></span>
|
style="background:rgb(34,197,94)"></span>
|
||||||
<span class="text-sm text-gray-600 dark:text-gray-300">{% trans "Gross" %}:</span>
|
<span class="text-sm text-gray-600 dark:text-gray-300">{% trans "Gross" %}:</span>
|
||||||
<span class="font-semibold">{{ gross }}</span>
|
<span class="font-semibold">{% if currency_symbol %}{{ currency_symbol }}{% endif %}{{ gross }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="inline-block w-3 h-3 rounded-sm"
|
<span class="inline-block w-3 h-3 rounded-sm"
|
||||||
style="background:rgb(239,68,68)"></span>
|
style="background:rgb(239,68,68)"></span>
|
||||||
<span class="text-sm text-gray-600 dark:text-gray-300">{% trans "Returns" %}:</span>
|
<span class="text-sm text-gray-600 dark:text-gray-300">{% trans "Returns" %}:</span>
|
||||||
<span class="font-semibold">{{ returns }}</span>
|
<span class="font-semibold">{% if currency_symbol %}{{ currency_symbol }}{% endif %}{{ returns }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from django.db.models import F, Sum
|
from constance import config
|
||||||
|
from django.db.models import F, QuerySet, Sum
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from constance import config
|
|
||||||
from engine.core.models import Order, OrderProduct
|
from engine.core.models import Order, OrderProduct
|
||||||
|
|
||||||
|
|
||||||
def get_period_order_products(period: timedelta = timedelta(days=30), statuses: list[str] | None = None):
|
def get_period_order_products(
|
||||||
|
period: timedelta = timedelta(days=30), statuses: list[str] | None = None
|
||||||
|
) -> QuerySet[OrderProduct]:
|
||||||
if statuses is None:
|
if statuses is None:
|
||||||
statuses = ["FINISHED"]
|
statuses = ["FINISHED"]
|
||||||
current = now()
|
current = now()
|
||||||
|
|
@ -16,7 +19,7 @@ def get_period_order_products(period: timedelta = timedelta(days=30), statuses:
|
||||||
return OrderProduct.objects.filter(status__in=statuses, order__in=orders)
|
return OrderProduct.objects.filter(status__in=statuses, order__in=orders)
|
||||||
|
|
||||||
|
|
||||||
def get_revenue(clear: bool = True, period: timedelta = timedelta(days=30)):
|
def get_revenue(clear: bool = True, period: timedelta = timedelta(days=30)) -> float:
|
||||||
order_products = get_period_order_products(period)
|
order_products = get_period_order_products(period)
|
||||||
total: float = (
|
total: float = (
|
||||||
order_products.aggregate(total=Coalesce(Sum(F("buy_price") * F("quantity")), 0.0)).get("total") or 0.0
|
order_products.aggregate(total=Coalesce(Sum(F("buy_price") * F("quantity")), 0.0)).get("total") or 0.0
|
||||||
|
|
@ -37,7 +40,7 @@ def get_revenue(clear: bool = True, period: timedelta = timedelta(days=30)):
|
||||||
return round(float(total), 2)
|
return round(float(total), 2)
|
||||||
|
|
||||||
|
|
||||||
def get_returns(period: timedelta = timedelta(days=30)):
|
def get_returns(period: timedelta = timedelta(days=30)) -> float:
|
||||||
order_products = get_period_order_products(period, ["RETURNED"])
|
order_products = get_period_order_products(period, ["RETURNED"])
|
||||||
total_returns: float = (
|
total_returns: float = (
|
||||||
order_products.aggregate(total=Coalesce(Sum(F("buy_price") * F("quantity")), 0.0)).get("total") or 0.0
|
order_products.aggregate(total=Coalesce(Sum(F("buy_price") * F("quantity")), 0.0)).get("total") or 0.0
|
||||||
|
|
@ -48,5 +51,5 @@ def get_returns(period: timedelta = timedelta(days=30)):
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
|
|
||||||
def get_total_processed_orders(period: timedelta = timedelta(days=30)):
|
def get_total_processed_orders(period: timedelta = timedelta(days=30)) -> int:
|
||||||
return get_period_order_products(period, ["RETURNED", "FINISHED"]).count()
|
return get_period_order_products(period, ["RETURNED", "FINISHED"]).count()
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ from django.core.exceptions import BadRequest
|
||||||
from django.db.models import Count, Sum
|
from django.db.models import Count, Sum
|
||||||
from django.http import FileResponse, Http404, HttpRequest, HttpResponse, HttpResponseRedirect, JsonResponse
|
from django.http import FileResponse, Http404, HttpRequest, HttpResponse, HttpResponseRedirect, JsonResponse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
|
from django.template import Context
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.http import urlsafe_base64_decode
|
from django.utils.http import urlsafe_base64_decode
|
||||||
|
|
@ -416,11 +417,16 @@ version.__doc__ = _( # type: ignore [assignment]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def dashboard_callback(request, context):
|
def dashboard_callback(request: HttpRequest, context: Context) -> Context:
|
||||||
revenue_gross_30 = get_revenue(clear=False)
|
revenue_gross_30: float = get_revenue(clear=False)
|
||||||
revenue_net_30 = get_revenue(clear=True)
|
revenue_net_30: float = get_revenue(clear=True)
|
||||||
returns_30 = get_returns()
|
returns_30: float = get_returns()
|
||||||
processed_orders_30 = get_total_processed_orders()
|
processed_orders_30: int = get_total_processed_orders()
|
||||||
|
currency_symbol: str = ""
|
||||||
|
with suppress(Exception):
|
||||||
|
currency_symbol = dict(getattr(settings, "CURRENCIES_WITH_SYMBOLS", ())).get(
|
||||||
|
getattr(settings, "CURRENCY_CODE", ""), ""
|
||||||
|
)
|
||||||
|
|
||||||
quick_links: list[dict[str, str]] = []
|
quick_links: list[dict[str, str]] = []
|
||||||
with suppress(Exception):
|
with suppress(Exception):
|
||||||
|
|
@ -454,7 +460,7 @@ def dashboard_callback(request, context):
|
||||||
if wished_first and wished_first.get("products"):
|
if wished_first and wished_first.get("products"):
|
||||||
product = Product.objects.filter(pk=wished_first["products"]).first()
|
product = Product.objects.filter(pk=wished_first["products"]).first()
|
||||||
if product:
|
if product:
|
||||||
img = product.images.first().image_url if product.images.exists() else ""
|
img = product.images.first().image_url if product.images.exists() else "" # type: ignore [union-attr]
|
||||||
most_wished = {
|
most_wished = {
|
||||||
"name": product.name,
|
"name": product.name,
|
||||||
"image": img,
|
"image": img,
|
||||||
|
|
@ -473,7 +479,7 @@ def dashboard_callback(request, context):
|
||||||
if not pid or pid not in product_by_id:
|
if not pid or pid not in product_by_id:
|
||||||
continue
|
continue
|
||||||
p = product_by_id[pid]
|
p = product_by_id[pid]
|
||||||
img = p.images.first().image_url if p.images.exists() else ""
|
img = p.images.first().image_url if p.images.exists() else "" # type: ignore [union-attr]
|
||||||
most_wished_list.append(
|
most_wished_list.append(
|
||||||
{
|
{
|
||||||
"name": p.name,
|
"name": p.name,
|
||||||
|
|
@ -498,7 +504,7 @@ def dashboard_callback(request, context):
|
||||||
if popular_first and popular_first.get("product"):
|
if popular_first and popular_first.get("product"):
|
||||||
product = Product.objects.filter(pk=popular_first["product"]).first()
|
product = Product.objects.filter(pk=popular_first["product"]).first()
|
||||||
if product:
|
if product:
|
||||||
img = product.images.first().image_url if product.images.exists() else ""
|
img = product.images.first().image_url if product.images.exists() else "" # type: ignore [union-attr]
|
||||||
most_popular = {
|
most_popular = {
|
||||||
"name": product.name,
|
"name": product.name,
|
||||||
"image": img,
|
"image": img,
|
||||||
|
|
@ -515,7 +521,7 @@ def dashboard_callback(request, context):
|
||||||
if not pid or pid not in product_by_id:
|
if not pid or pid not in product_by_id:
|
||||||
continue
|
continue
|
||||||
p = product_by_id[pid]
|
p = product_by_id[pid]
|
||||||
img = p.images.first().image_url if p.images.exists() else ""
|
img = p.images.first().image_url if p.images.exists() else "" # type: ignore [union-attr]
|
||||||
most_popular_list.append(
|
most_popular_list.append(
|
||||||
{
|
{
|
||||||
"name": p.name,
|
"name": p.name,
|
||||||
|
|
@ -538,6 +544,7 @@ def dashboard_callback(request, context):
|
||||||
"most_popular_product": most_popular,
|
"most_popular_product": most_popular,
|
||||||
"most_wished_products": most_wished_list,
|
"most_wished_products": most_wished_list,
|
||||||
"most_popular_products": most_popular_list,
|
"most_popular_products": most_popular_list,
|
||||||
|
"currency_symbol": currency_symbol,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,21 @@
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from django.templatetags.static import static
|
from django.templatetags.static import static
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from evibes.settings.base import (
|
from evibes.settings.base import (
|
||||||
|
LANGUAGES as BASE_LANGUAGES,
|
||||||
|
)
|
||||||
|
from evibes.settings.base import (
|
||||||
|
LANGUAGES_FLAGS,
|
||||||
PROJECT_NAME,
|
PROJECT_NAME,
|
||||||
STOREFRONT_DOMAIN,
|
STOREFRONT_DOMAIN,
|
||||||
SUPPORT_CONTACT,
|
SUPPORT_CONTACT,
|
||||||
TASKBOARD_URL,
|
TASKBOARD_URL,
|
||||||
LANGUAGES as BASE_LANGUAGES,
|
|
||||||
LANGUAGES_FLAGS,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
UNFOLD = {
|
UNFOLD: dict[str, Any] = {
|
||||||
"SITE_URL": STOREFRONT_DOMAIN,
|
"SITE_URL": STOREFRONT_DOMAIN,
|
||||||
"SITE_TITLE": f"{PROJECT_NAME} Dashboard",
|
"SITE_TITLE": f"{PROJECT_NAME} Dashboard",
|
||||||
"SITE_HEADER": PROJECT_NAME,
|
"SITE_HEADER": PROJECT_NAME,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue