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)" %}
|
||||
{% endcomponent %}
|
||||
{% component "unfold/components/title.html" %}
|
||||
{{ revenue_gross_30|default:0 }}
|
||||
{% if currency_symbol %}{{ currency_symbol }}{% endif %}{{ revenue_gross_30|default:0 }}
|
||||
{% endcomponent %}
|
||||
{% endcomponent %}
|
||||
|
||||
|
|
@ -36,7 +36,7 @@
|
|||
{% trans "Revenue (net, 30d)" %}
|
||||
{% endcomponent %}
|
||||
{% component "unfold/components/title.html" %}
|
||||
{{ revenue_net_30|default:0 }}
|
||||
{% if currency_symbol %}{{ currency_symbol }}{% endif %}{{ revenue_net_30|default:0 }}
|
||||
{% endcomponent %}
|
||||
{% endcomponent %}
|
||||
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
{% trans "Returns (30d)" %}
|
||||
{% endcomponent %}
|
||||
{% component "unfold/components/title.html" %}
|
||||
{{ returns_30|default:0 }}
|
||||
{% if currency_symbol %}{{ currency_symbol }}{% endif %}{{ returns_30|default:0 }}
|
||||
{% endcomponent %}
|
||||
{% endcomponent %}
|
||||
|
||||
|
|
@ -83,13 +83,13 @@
|
|||
<span class="inline-block w-3 h-3 rounded-sm"
|
||||
style="background:rgb(34,197,94)"></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 class="flex items-center gap-2">
|
||||
<span class="inline-block w-3 h-3 rounded-sm"
|
||||
style="background:rgb(239,68,68)"></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>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
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.utils.timezone import now
|
||||
from constance import config
|
||||
|
||||
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:
|
||||
statuses = ["FINISHED"]
|
||||
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)
|
||||
|
||||
|
||||
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)
|
||||
total: float = (
|
||||
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)
|
||||
|
||||
|
||||
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"])
|
||||
total_returns: float = (
|
||||
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
|
||||
|
||||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from django.core.exceptions import BadRequest
|
|||
from django.db.models import Count, Sum
|
||||
from django.http import FileResponse, Http404, HttpRequest, HttpResponse, HttpResponseRedirect, JsonResponse
|
||||
from django.shortcuts import redirect
|
||||
from django.template import Context
|
||||
from django.urls import reverse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.http import urlsafe_base64_decode
|
||||
|
|
@ -416,11 +417,16 @@ version.__doc__ = _( # type: ignore [assignment]
|
|||
)
|
||||
|
||||
|
||||
def dashboard_callback(request, context):
|
||||
revenue_gross_30 = get_revenue(clear=False)
|
||||
revenue_net_30 = get_revenue(clear=True)
|
||||
returns_30 = get_returns()
|
||||
processed_orders_30 = get_total_processed_orders()
|
||||
def dashboard_callback(request: HttpRequest, context: Context) -> Context:
|
||||
revenue_gross_30: float = get_revenue(clear=False)
|
||||
revenue_net_30: float = get_revenue(clear=True)
|
||||
returns_30: float = get_returns()
|
||||
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]] = []
|
||||
with suppress(Exception):
|
||||
|
|
@ -454,7 +460,7 @@ def dashboard_callback(request, context):
|
|||
if wished_first and wished_first.get("products"):
|
||||
product = Product.objects.filter(pk=wished_first["products"]).first()
|
||||
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 = {
|
||||
"name": product.name,
|
||||
"image": img,
|
||||
|
|
@ -473,7 +479,7 @@ def dashboard_callback(request, context):
|
|||
if not pid or pid not in product_by_id:
|
||||
continue
|
||||
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(
|
||||
{
|
||||
"name": p.name,
|
||||
|
|
@ -498,7 +504,7 @@ def dashboard_callback(request, context):
|
|||
if popular_first and popular_first.get("product"):
|
||||
product = Product.objects.filter(pk=popular_first["product"]).first()
|
||||
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 = {
|
||||
"name": product.name,
|
||||
"image": img,
|
||||
|
|
@ -515,7 +521,7 @@ def dashboard_callback(request, context):
|
|||
if not pid or pid not in product_by_id:
|
||||
continue
|
||||
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(
|
||||
{
|
||||
"name": p.name,
|
||||
|
|
@ -538,6 +544,7 @@ def dashboard_callback(request, context):
|
|||
"most_popular_product": most_popular,
|
||||
"most_wished_products": most_wished_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.urls import reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from evibes.settings.base import (
|
||||
LANGUAGES as BASE_LANGUAGES,
|
||||
)
|
||||
from evibes.settings.base import (
|
||||
LANGUAGES_FLAGS,
|
||||
PROJECT_NAME,
|
||||
STOREFRONT_DOMAIN,
|
||||
SUPPORT_CONTACT,
|
||||
TASKBOARD_URL,
|
||||
LANGUAGES as BASE_LANGUAGES,
|
||||
LANGUAGES_FLAGS,
|
||||
)
|
||||
|
||||
UNFOLD = {
|
||||
UNFOLD: dict[str, Any] = {
|
||||
"SITE_URL": STOREFRONT_DOMAIN,
|
||||
"SITE_TITLE": f"{PROJECT_NAME} Dashboard",
|
||||
"SITE_HEADER": PROJECT_NAME,
|
||||
|
|
|
|||
Loading…
Reference in a new issue