Coverage for jacc/interests.py: 90%

# Coverage for jacc/interests.py : 90%

## 49 statements 44 run 5 missing 0 excluded

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

1 from datetime import date, time, datetime

2 from decimal import Decimal

3 from typing import Optional

4 import pytz

5 from django.utils.timezone import now

6 from jacc.models import AccountEntry

7

8

9 def 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()

22

23 bal = None

24 cur_date = None

25 daily_rate = rate_pct / Decimal(36500)

26 accum_interest = Decimal('0.00')

27 done = False

28

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)

39

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

46

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

67

68 return accum_interest