Merge branch 'dev' into main
This commit is contained in:
commit
e18b1f48d9
@ -3,9 +3,10 @@ from decimal import Decimal
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
|
from functools import cached_property
|
||||||
|
from django.utils.text import slugify
|
||||||
from django.db.models.fields import related
|
from django.db.models.fields import related
|
||||||
from django.db.models.functions import ExtractMonth
|
from django.db.models.functions import ExtractMonth
|
||||||
from django.utils.text import slugify
|
|
||||||
|
|
||||||
|
|
||||||
class Category(models.Model):
|
class Category(models.Model):
|
||||||
@ -357,7 +358,15 @@ class Goal(models.Model):
|
|||||||
|
|
||||||
return self.start_date
|
return self.start_date
|
||||||
|
|
||||||
|
@cached_property
|
||||||
def progress(self):
|
def progress(self):
|
||||||
|
"""Progreso del objetivo. Memoizado: se calcula una vez por instancia.
|
||||||
|
|
||||||
|
Ojo: es una cached_property, no un método. Se accede como `goal.progress`
|
||||||
|
(sin paréntesis), tanto en código como en plantillas. El valor se cachea
|
||||||
|
en la instancia la primera vez; si en la misma petición se modifican
|
||||||
|
gastos y se necesita recalcular, habría que crear una instancia nueva.
|
||||||
|
"""
|
||||||
if self.kind == self.KIND_SAVING:
|
if self.kind == self.KIND_SAVING:
|
||||||
# Provisional: saldo de la cuenta asociada. Cuando exista el
|
# Provisional: saldo de la cuenta asociada. Cuando exista el
|
||||||
# módulo de inversiones, esta rama es lo único que hay que tocar.
|
# módulo de inversiones, esta rama es lo único que hay que tocar.
|
||||||
@ -384,7 +393,7 @@ class Goal(models.Model):
|
|||||||
def percentage(self):
|
def percentage(self):
|
||||||
if not self.target_amount:
|
if not self.target_amount:
|
||||||
return 0
|
return 0
|
||||||
return (self.progress() / self.target_amount) * 100
|
return (self.progress / self.target_amount) * 100
|
||||||
|
|
||||||
def bar_width(self):
|
def bar_width(self):
|
||||||
return min(float(self.percentage()), 100)
|
return min(float(self.percentage()), 100)
|
||||||
@ -405,7 +414,7 @@ class Goal(models.Model):
|
|||||||
"""True si es un presupuesto que ya se ha pasado del límite."""
|
"""True si es un presupuesto que ya se ha pasado del límite."""
|
||||||
return (
|
return (
|
||||||
self.kind == self.KIND_BUDGET
|
self.kind == self.KIND_BUDGET
|
||||||
and self.progress() > self.target_amount
|
and self.progress > self.target_amount
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|||||||
@ -24,7 +24,7 @@ def test_payment_goal_sums_expenses_since_start_date(user, account, category):
|
|||||||
include_subcategories=False, start_date=start, target_amount=Decimal("100"),
|
include_subcategories=False, start_date=start, target_amount=Decimal("100"),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert goal.progress() == Decimal("20")
|
assert goal.progress == Decimal("20")
|
||||||
|
|
||||||
|
|
||||||
def test_payment_goal_include_subcategories_true_includes_child_expenses(user, account, category):
|
def test_payment_goal_include_subcategories_true_includes_child_expenses(user, account, category):
|
||||||
@ -36,7 +36,7 @@ def test_payment_goal_include_subcategories_true_includes_child_expenses(user, a
|
|||||||
include_subcategories=True, target_amount=Decimal("100"),
|
include_subcategories=True, target_amount=Decimal("100"),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert goal.progress() == Decimal("15")
|
assert goal.progress == Decimal("15")
|
||||||
|
|
||||||
|
|
||||||
def test_payment_goal_include_subcategories_false_excludes_child_expenses(user, account, category):
|
def test_payment_goal_include_subcategories_false_excludes_child_expenses(user, account, category):
|
||||||
@ -48,7 +48,7 @@ def test_payment_goal_include_subcategories_false_excludes_child_expenses(user,
|
|||||||
include_subcategories=False, target_amount=Decimal("100"),
|
include_subcategories=False, target_amount=Decimal("100"),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert goal.progress() == Decimal("0")
|
assert goal.progress == Decimal("0")
|
||||||
|
|
||||||
|
|
||||||
def test_budget_goal_period_month_counts_only_current_month(user, account, category):
|
def test_budget_goal_period_month_counts_only_current_month(user, account, category):
|
||||||
@ -60,7 +60,7 @@ def test_budget_goal_period_month_counts_only_current_month(user, account, categ
|
|||||||
period=Goal.PERIOD_MONTH, target_amount=Decimal("100"),
|
period=Goal.PERIOD_MONTH, target_amount=Decimal("100"),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert goal.progress() == Decimal("30")
|
assert goal.progress == Decimal("30")
|
||||||
|
|
||||||
|
|
||||||
def test_budget_goal_is_exceeded_true_over_target(user, account, category):
|
def test_budget_goal_is_exceeded_true_over_target(user, account, category):
|
||||||
@ -92,7 +92,7 @@ def test_is_exceeded_always_false_for_non_budget_kind(user, account, category):
|
|||||||
owner=user, name="Pago", kind=Goal.KIND_PAYMENT, category=category, target_amount=Decimal("10"),
|
owner=user, name="Pago", kind=Goal.KIND_PAYMENT, category=category, target_amount=Decimal("10"),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert goal.progress() > goal.target_amount
|
assert goal.progress > goal.target_amount
|
||||||
assert goal.is_exceeded() is False
|
assert goal.is_exceeded() is False
|
||||||
|
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ def test_saving_goal_progress_equals_account_balance(user):
|
|||||||
owner=user, name="Ahorro", kind=Goal.KIND_SAVING, account=acc, target_amount=Decimal("1000"),
|
owner=user, name="Ahorro", kind=Goal.KIND_SAVING, account=acc, target_amount=Decimal("1000"),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert goal.progress() == acc.current_balance()
|
assert goal.progress == acc.current_balance()
|
||||||
|
|
||||||
|
|
||||||
def test_bar_width_caps_at_100_above_target(user):
|
def test_bar_width_caps_at_100_above_target(user):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user