Coverage for jacc/interests.py : 90%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from datetime import date, time, datetime
2from decimal import Decimal
3from typing import Optional
4import pytz
5from django.utils.timezone import now
6from jacc.models import AccountEntry
9def calculate_simple_interest(entries, rate_pct: Decimal, # pylint: disable=too-many-locals
10 interest_date: Optional[date] = None, begin: Optional[date] = None) -> Decimal:
11 """
12 Calculates simple interest of specified entries over time.
13 Does not accumulate interest to interest.
14 :param entries: AccountEntry iterable (e.g. list/QuerySet) ordered by timestamp (ascending)
15 :param rate_pct: Interest rate %, e.g. 8.00 for 8%
16 :param interest_date: Interest end date. Default is current date.
17 :param begin: Optional begin date for the interest. Default is whole range from the timestamp of account entries.
18 :return: Decimal accumulated interest
19 """
20 if interest_date is None:
21 interest_date = now().date()
23 bal = None
24 cur_date = None
25 daily_rate = rate_pct / Decimal(36500)
26 accum_interest = Decimal('0.00')
27 done = False
29 entries_list = list(entries)
30 nentries = len(entries_list)
31 if nentries > 0:
32 # make sure we calculate interest over whole range until interest_date
33 last = entries_list[nentries-1]
34 assert isinstance(last, AccountEntry)
35 if last.timestamp.date() < interest_date:
36 timestamp = pytz.utc.localize(datetime.combine(interest_date, time(0, 0)))
37 e = AccountEntry(timestamp=timestamp, amount=Decimal('0.00'), type=last.type)
38 entries_list.append(e)
40 # initial values from the first account entry
41 e = entries_list[0]
42 bal = e.amount or Decimal('0.00')
43 cur_date = e.timestamp.date()
44 if begin and begin > cur_date:
45 cur_date = begin
47 for e in entries_list[1:]:
48 assert isinstance(e, AccountEntry)
49 next_date = e.timestamp.date()
50 if begin and begin > next_date:
51 next_date = begin
52 if next_date > interest_date:
53 next_date = interest_date
54 done = True
55 assert cur_date
56 assert bal is not None
57 time_days = (next_date - cur_date).days
58 if time_days > 0:
59 day_interest = bal * daily_rate
60 interval_interest = day_interest * Decimal(time_days)
61 accum_interest += interval_interest
62 cur_date = next_date
63 if e.amount is not None:
64 bal += e.amount
65 if done:
66 break
68 return accum_interest