diff --git a/app/apps/insights/forms.py b/app/apps/insights/forms.py index 34c6c8f..50d8428 100644 --- a/app/apps/insights/forms.py +++ b/app/apps/insights/forms.py @@ -9,6 +9,7 @@ from apps.common.widgets.datepicker import ( AirDatePickerInput, ) from apps.transactions.models import TransactionCategory +from apps.common.widgets.tom_select import TomSelect class SingleMonthForm(forms.Form): @@ -115,7 +116,9 @@ class CategoryForm(forms.Form): category = forms.ModelChoiceField( required=False, label=_("Category"), + empty_label=_("Uncategorized"), queryset=TransactionCategory.objects.filter(active=True), + widget=TomSelect(clear_button=True), ) def __init__(self, *args, **kwargs): diff --git a/app/apps/insights/utils/category_explorer.py b/app/apps/insights/utils/category_explorer.py index 639750c..f7dc401 100644 --- a/app/apps/insights/utils/category_explorer.py +++ b/app/apps/insights/utils/category_explorer.py @@ -3,7 +3,7 @@ from django.db.models.functions import Coalesce from django.utils.translation import gettext_lazy as _ -def get_category_sums_by_account(queryset, category): +def get_category_sums_by_account(queryset, category=None): """ Returns income/expense sums per account for a specific category. """ @@ -11,10 +11,11 @@ def get_category_sums_by_account(queryset, category): queryset.filter(category=category) .values("account__name") .annotate( - income=Coalesce( + current_income=Coalesce( Sum( Case( When(type="IN", then="amount"), + When(is_paid=True, then="amount"), default=Value(0), output_field=DecimalField(max_digits=42, decimal_places=30), ) @@ -22,10 +23,35 @@ def get_category_sums_by_account(queryset, category): Value(0), output_field=DecimalField(max_digits=42, decimal_places=30), ), - expense=Coalesce( + current_expense=Coalesce( Sum( Case( When(type="EX", then=-F("amount")), + When(is_paid=True, then="amount"), + default=Value(0), + output_field=DecimalField(max_digits=42, decimal_places=30), + ) + ), + Value(0), + output_field=DecimalField(max_digits=42, decimal_places=30), + ), + projected_income=Coalesce( + Sum( + Case( + When(type="IN", then="amount"), + When(is_paid=False, then="amount"), + default=Value(0), + output_field=DecimalField(max_digits=42, decimal_places=30), + ) + ), + Value(0), + output_field=DecimalField(max_digits=42, decimal_places=30), + ), + projected_expense=Coalesce( + Sum( + Case( + When(type="EX", then=-F("amount")), + When(is_paid=False, then="amount"), default=Value(0), output_field=DecimalField(max_digits=42, decimal_places=30), ) @@ -41,18 +67,26 @@ def get_category_sums_by_account(queryset, category): "labels": [item["account__name"] for item in sums], "datasets": [ { - "label": _("Income"), - "data": [float(item["income"]) for item in sums], + "label": _("Current Income"), + "data": [float(item["current_income"]) for item in sums], }, { - "label": _("Expenses"), - "data": [float(item["expense"]) for item in sums], + "label": _("Current Expenses"), + "data": [float(item["current_expense"]) for item in sums], + }, + { + "label": _("Projected Income"), + "data": [float(item["projected_income"]) for item in sums], + }, + { + "label": _("Projected Expenses"), + "data": [float(item["projected_expense"]) for item in sums], }, ], } -def get_category_sums_by_currency(queryset, category): +def get_category_sums_by_currency(queryset, category=None): """ Returns income/expense sums per currency for a specific category. """ @@ -60,10 +94,11 @@ def get_category_sums_by_currency(queryset, category): queryset.filter(category=category) .values("account__currency__name") .annotate( - income=Coalesce( + current_income=Coalesce( Sum( Case( When(type="IN", then="amount"), + When(is_paid=True, then="amount"), default=Value(0), output_field=DecimalField(max_digits=42, decimal_places=30), ) @@ -71,10 +106,35 @@ def get_category_sums_by_currency(queryset, category): Value(0), output_field=DecimalField(max_digits=42, decimal_places=30), ), - expense=Coalesce( + current_expense=Coalesce( Sum( Case( When(type="EX", then=-F("amount")), + When(is_paid=True, then="amount"), + default=Value(0), + output_field=DecimalField(max_digits=42, decimal_places=30), + ) + ), + Value(0), + output_field=DecimalField(max_digits=42, decimal_places=30), + ), + projected_income=Coalesce( + Sum( + Case( + When(type="IN", then="amount"), + When(is_paid=False, then="amount"), + default=Value(0), + output_field=DecimalField(max_digits=42, decimal_places=30), + ) + ), + Value(0), + output_field=DecimalField(max_digits=42, decimal_places=30), + ), + projected_expense=Coalesce( + Sum( + Case( + When(type="EX", then=-F("amount")), + When(is_paid=False, then="amount"), default=Value(0), output_field=DecimalField(max_digits=42, decimal_places=30), ) @@ -90,12 +150,20 @@ def get_category_sums_by_currency(queryset, category): "labels": [item["account__currency__name"] for item in sums], "datasets": [ { - "label": _("Income"), - "data": [float(item["income"]) for item in sums], + "label": _("Current Income"), + "data": [float(item["current_income"]) for item in sums], }, { - "label": _("Expenses"), - "data": [float(item["expense"]) for item in sums], + "label": _("Current Expenses"), + "data": [float(item["current_expense"]) for item in sums], + }, + { + "label": _("Projected Income"), + "data": [float(item["projected_income"]) for item in sums], + }, + { + "label": _("Projected Expenses"), + "data": [float(item["projected_expense"]) for item in sums], }, ], } diff --git a/app/apps/insights/views.py b/app/apps/insights/views.py index a9e8513..c6738cc 100644 --- a/app/apps/insights/views.py +++ b/app/apps/insights/views.py @@ -126,7 +126,7 @@ def category_sum_by_account(request): # Generate data account_data = get_category_sums_by_account(transactions, category) else: - account_data = None + account_data = get_category_sums_by_account(transactions, category=None) return render( request, @@ -150,7 +150,7 @@ def category_sum_by_currency(request): # Generate data currency_data = get_category_sums_by_currency(transactions, category) else: - currency_data = None + currency_data = get_category_sums_by_currency(transactions, category=None) return render( request, diff --git a/app/templates/cotton/transaction/item.html b/app/templates/cotton/transaction/item.html index 21f1fda..1fa4f35 100644 --- a/app/templates/cotton/transaction/item.html +++ b/app/templates/cotton/transaction/item.html @@ -1,7 +1,7 @@ {% load markdown %} {% load i18n %} -
-
+
+
{% if not disable_selection %}
@@ -28,7 +28,6 @@
-
diff --git a/app/templates/insights/pages/index.html b/app/templates/insights/pages/index.html index 3afbe45..3ec8aa3 100644 --- a/app/templates/insights/pages/index.html +++ b/app/templates/insights/pages/index.html @@ -4,11 +4,12 @@ {% block content %}
-
+
-
-
+
+
- - + id="picker-type"> + + - - + + - - + + - - + + - - -
-
+ +
+ -
- {% crispy month_form %} -
-
- {% crispy year_form %} -
-
- {% crispy month_range_form %} -
-
- {% crispy year_range_form %} -
-
- {% crispy date_range_form %} -
- -
-
- +
+