Tracks budget based on paydate and when bills are due, lets you know how much you can spend

budget.py 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. #!/usr/bin/python3
  2. import datetime
  3. from decimal import *
  4. from settings import DATA
  5. from prettytable import PrettyTable
  6. TWOPLACES = Decimal(10) ** -2
  7. def daterange(start_date, end_date):
  8. for n in range(int ((end_date - start_date).days)):
  9. yield start_date + datetime.timedelta(n)
  10. class PayPeriod:
  11. def __init__(self, calculate=False):
  12. self.bills = []
  13. self.expenses = DATA.get('expenses')
  14. self.delta = None
  15. self.beginning = None
  16. self.end = None
  17. self.total_bills = None
  18. self.money_left = None
  19. self.total_expenses = None
  20. if calculate:
  21. self.calculate_budget()
  22. def lookup_bills(self):
  23. if not self.beginning or not self.end:
  24. self.current_pay_period()
  25. bill_data = DATA.get('bills')
  26. for day in daterange(self.beginning, self.end):
  27. x = bill_data.get(day.day)
  28. if x:
  29. for bill in x:
  30. self.bills.append(bill)
  31. def pay_period_delta(self):
  32. period = DATA.get('pay_period')
  33. if not period:
  34. raise Error
  35. unit = period.get('unit')
  36. value = period.get('value')
  37. if unit == 'week':
  38. self.delta = datetime.timedelta(weeks=value)
  39. elif unit == 'day':
  40. self.delta = datetime.timedelta(days=value)
  41. elif unit == 'month':
  42. self.delta = value
  43. else:
  44. raise Error
  45. def current_pay_period(self):
  46. self.find_pay_period(datetime.date.today())
  47. def next_pay_period(self):
  48. if self.delta == None:
  49. self.pay_period_delta()
  50. next_period = PayPeriod()
  51. next_period.find_pay_period(datetime.date.today() + self.delta)
  52. next_period.calculate_budget()
  53. return next_period
  54. def find_pay_period(self, date):
  55. if self.delta == None:
  56. self.pay_period_delta()
  57. pay_date_str = DATA.get('pay_date')
  58. if not pay_date_str:
  59. raise Error
  60. pay_period = DATA.get('pay_period')
  61. if not pay_period:
  62. raise Error
  63. pay_date_list = pay_date_str.split('/')
  64. pay_date = datetime.date(int(pay_date_list[0]), int(pay_date_list[1]), int(pay_date_list[2]))
  65. beginning = pay_date
  66. end = pay_date
  67. while date > end:
  68. beginning = end
  69. end += self.delta
  70. self.beginning = beginning
  71. self.end = end
  72. def calculate_budget(self):
  73. if not self.bills:
  74. self.lookup_bills()
  75. self.total_bills = Decimal(0)
  76. for bill in self.bills:
  77. self.total_bills += bill.get('amt')
  78. self.total_expenses = Decimal(0)
  79. for expense in self.expenses:
  80. self.total_expenses += expense.get('amt')
  81. self.money_left = DATA.get("pay_amount") - self.total_bills - self.total_expenses
  82. def print_budget(self):
  83. if not self.total_bills:
  84. self.calculate_budget()
  85. print("\nBills")
  86. print("***************************")
  87. bills_table = PrettyTable(["Description", "Amount"])
  88. bills_table.align["Amount"] = "r"
  89. for bill in self.bills:
  90. bills_table.add_row([bill.get('desc'), bill.get('amt').quantize(TWOPLACES)])
  91. bills_table.add_row(["----------", "-----"])
  92. bills_table.add_row(["Total", self.total_bills.quantize(TWOPLACES)])
  93. print(bills_table)
  94. print("\nExpenses")
  95. print("***************************")
  96. expenses_table = PrettyTable(["Description", "Amount"])
  97. expenses_table.align["Amount"] = "r"
  98. for expense in self.expenses:
  99. expenses_table.add_row([expense.get('desc'), expense.get('amt').quantize(TWOPLACES)])
  100. expenses_table.add_row(["----------", "-----"])
  101. expenses_table.add_row(["Total", self.total_expenses.quantize(TWOPLACES)])
  102. print(expenses_table)
  103. print("\nTotals")
  104. print("***************************")
  105. print("Expenses Total: " + str(self.total_expenses.quantize(TWOPLACES)))
  106. print("Money After Bills + Expenses: " + str(self.money_left.quantize(TWOPLACES)))
  107. next_period = self.next_pay_period()
  108. money_left_next = next_period.money_left
  109. print("Money After Bills + Expenses Next: " + str(money_left_next.quantize(TWOPLACES)))
  110. if money_left_next <= 0:
  111. print("Not enough money left next paycheck!! Expenses being rolled together!")
  112. self.expenses += next_period.expenses
  113. self.calculate_budget()
  114. self.money_left = self.money_left + money_left_next + next_period.total_expenses
  115. print("Money left after combining: " + str(self.money_left.quantize(TWOPLACES)))
  116. print("\nPercent Expenses")
  117. print("***************************")
  118. total_perc_expenses = Decimal(0)
  119. perc_expenses = PrettyTable(["Description", "Amount"])
  120. perc_expenses.align["Amount"] = "r"
  121. for perc_expense in DATA.get("perc_expenses", []):
  122. amt = perc_expense.get('perc') * self.money_left
  123. total_perc_expenses += amt
  124. perc_expenses.add_row([perc_expense.get('desc'), amt.quantize(TWOPLACES)])
  125. perc_expenses.add_row(["----------", "-----"])
  126. perc_expenses.add_row(["Total", total_perc_expenses.quantize(TWOPLACES)])
  127. print(perc_expenses)
  128. money_left_perc = self.money_left - total_perc_expenses
  129. print("\nAllowances")
  130. print("***************************")
  131. allowance_table = PrettyTable(["Description", "Amount"])
  132. allowance_table.align["Amount"] = "r"
  133. for allowance in DATA.get("allowances", []):
  134. amt = allowance.get('perc') * money_left_perc
  135. allowance_table.add_row([allowance.get('desc'), amt.quantize(TWOPLACES)])
  136. print(allowance_table)
  137. pay_period = PayPeriod()
  138. pay_period.print_budget()