Skip to content

Commit a5f05c7

Browse files
committed
Improvements of update_running_totals management command
1 parent 82b47a0 commit a5f05c7

File tree

4 files changed

+66
-26
lines changed

4 files changed

+66
-26
lines changed

hordak/management/commands/recalculate_running_totals.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ def add_arguments(self, parser):
2424
)
2525

2626
def handle(self, *args, **options):
27-
print("Recalculating running totals for all accounts")
28-
all_values_are_correct = True
27+
print(
28+
f"{'Checking' if options['check'] else 'Recalculating'} running totals for all accounts"
29+
)
30+
output_string = ""
2931
# We are using Legs subquery because it is quicker
3032
queryset = Account.objects.filter(pk__in=Leg.objects.values("account"))
3133
i = 0
@@ -34,14 +36,20 @@ def handle(self, *args, **options):
3436
i += 1
3537
if i % 100 == 0:
3638
print(f"Processed {i} accounts")
37-
value_correct = account.update_running_totals(check_only=options["check"])
38-
if not value_correct:
39-
all_values_are_correct = False
39+
faulty_values = account.update_running_totals(check_only=options["check"])
40+
if faulty_values:
41+
for currency, rt_value, correct_value in faulty_values:
42+
output_string += f"Account {account.name} has faulty running total for {currency}"
43+
output_string += f" (should be {correct_value}, is {rt_value})\n"
4044

41-
if options["mail_admins"] and not all_values_are_correct:
45+
if options["mail_admins"] and output_string:
4246
mail_admins(
4347
"Running totals are incorrect",
44-
"Running totals are incorrect for some accounts",
48+
f"Running totals are incorrect for some accounts\n\n{output_string}",
4549
)
4650

47-
return 0 if all_values_are_correct else "Running totals are incorrect"
51+
return (
52+
f"Running totals are INCORRECT: \n\n{output_string}"
53+
if output_string
54+
else "Running totals are correct"
55+
)

hordak/models/core.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,12 @@ class RunningTotal(models.Model):
6868
This is used to speed up the calculation of account balances.
6969
7070
This field should be considered an estimated value calculated for performance reasons.
71-
It is not guaranteed to be accurate.
71+
It is not guaranteed to be accurate:
72+
* Running totals are calculated through post_delete and pre_save signals on the Transaction model.
73+
This means that they are not updated when a transaction is mass updated.
74+
* They don't take into account children accounts - the total value of a parent
75+
account needs to be calculated from its children manually now
7276
73-
Running totals are calculated through post_delete and pre_save signals on the Transaction model.
74-
This means that they are not updated when a transaction is mass updated.
7577
"""
7678

7779
account = models.ForeignKey(
@@ -386,30 +388,46 @@ def transfer_to(self, to_account, amount, **transaction_kwargs):
386388
return transaction
387389

388390
def update_running_totals(self, check_only=False):
389-
"""Update the running totals for this account"""
390-
all_values_are_correct = True
391+
"""
392+
Update the running totals for this account by counting all transactions
391393
394+
Args:
395+
check_only (bool): If true, don't actually update the running totals,
396+
just check that they are correct.
397+
Returns:
398+
list: A list of currencies that have been updated or didn't pass the check
399+
"""
392400
total = self.balance()
401+
faulty_values = []
402+
393403
for money in total.monies():
394404
currency = money.currency.code
405+
if total[currency].amount == 0:
406+
continue
395407
try:
396408
running_total = self.running_totals.get(currency=currency)
397409
if running_total.balance != total[currency]:
398410
print(
399411
f"Running total for {self} ({currency}) is "
400412
f"{running_total.balance} but should be {total[currency]}"
401413
)
402-
all_values_are_correct = False
414+
faulty_values.append(
415+
(currency, running_total.balance, total[currency])
416+
)
417+
if not check_only:
418+
self.running_totals.filter(currency=currency).update(
419+
balance=total[currency]
420+
)
403421
except RunningTotal.DoesNotExist:
404422
print(f"No running total for {self} ({currency})")
405-
all_values_are_correct = False
406-
if not check_only:
407-
RunningTotal.objects.update_or_create(
408-
account=self,
409-
currency=currency,
410-
defaults={"balance": total[currency]},
411-
)
412-
return all_values_are_correct
423+
faulty_values.append((currency, None, total[currency]))
424+
if not check_only:
425+
RunningTotal.objects.create(
426+
account=self,
427+
currency=currency,
428+
balance=total[currency],
429+
)
430+
return faulty_values
413431

414432

415433
class TransactionManager(models.Manager):

hordak/tests/models/test_core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def test_str_child(self):
6060
def test_update_running_totals_no_money(self):
6161
account = self.account()
6262
account.update_running_totals()
63-
self.assertEqual(account.running_totals.get().balance, Money(0, "EUR"))
63+
self.assertQuerysetEqual(account.running_totals.all(), [])
6464

6565
def test_str_currency(self):
6666
account = self.account(currencies=["EUR", "GBP"])

hordak/tests/test_commands.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,11 @@ def test_check_mismatch(self):
108108
running_total.save()
109109

110110
ret_val = call_command("recalculate_running_totals", *["--check"])
111-
self.assertEqual(ret_val, "Running totals are incorrect")
111+
self.assertEqual(
112+
ret_val,
113+
"Running totals are INCORRECT: \n\n"
114+
"Account Account 1 has faulty running total for EUR (should be € 100.00, is €200.00)\n"
115+
)
112116

113117
def test_mail_admins(self):
114118
"""
@@ -131,8 +135,18 @@ def test_mail_admins(self):
131135
running_total.save()
132136

133137
ret_val = call_command("recalculate_running_totals", *["--mail-admins"])
134-
self.assertEqual(ret_val, "Running totals are incorrect")
138+
self.assertEqual(
139+
ret_val,
140+
"Running totals are INCORRECT: \n\n"
141+
"Account Account 1 has faulty running total for EUR (should be € 100.00, is €200.00)\n"
142+
)
135143
self.assertEqual(len(mail.outbox), 1)
136144
self.assertEqual(
137-
mail.outbox[0].subject, "[Django] Running totals are incorrect"
145+
mail.outbox[0].subject,
146+
"[Django] Running totals are incorrect"
147+
)
148+
self.assertEqual(
149+
mail.outbox[0].body,
150+
"Running totals are incorrect for some accounts\n\n"
151+
"Account Account 1 has faulty running total for EUR (should be € 100.00, is €200.00)\n"
138152
)

0 commit comments

Comments
 (0)