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

budget.py 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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. if self.delta == None:
  47. self.pay_period_delta()
  48. self.find_pay_period(datetime.date.today())
  49. def next_pay_period(self):
  50. if self.delta == None:
  51. self.pay_period_delta()
  52. next_period = PayPeriod()
  53. next_period.find_pay_period(self.end)
  54. next_period.calculate_budget()
  55. return next_period
  56. def find_pay_period(self, date):
  57. if self.delta == None:
  58. self.pay_period_delta()
  59. pay_date_str = DATA.get('pay_date')
  60. if not pay_date_str:
  61. raise Error
  62. pay_period = DATA.get('pay_period')
  63. if not pay_period:
  64. raise Error
  65. pay_date_list = pay_date_str.split('/')
  66. pay_date = datetime.date(int(pay_date_list[0]), int(pay_date_list[1]), int(pay_date_list[2]))
  67. beginning = pay_date
  68. end = pay_date
  69. while date >= end:
  70. beginning = end
  71. end += self.delta
  72. self.beginning = beginning
  73. self.end = end
  74. def calculate_budget(self):
  75. if not self.bills:
  76. self.lookup_bills()
  77. self.total_bills = Decimal(0)
  78. for bill in self.bills:
  79. self.total_bills += bill.get('amt')
  80. self.total_expenses = Decimal(0)
  81. for expense in self.expenses:
  82. self.total_expenses += expense.get('amt')
  83. self.money_left = DATA.get("pay_amount") - self.total_bills - self.total_expenses
  84. def print_budget(self):
  85. if not self.total_bills:
  86. self.calculate_budget()
  87. print("\nBills")
  88. print("***************************")
  89. bills_table = PrettyTable(["Description", "Amount"])
  90. bills_table.align["Amount"] = "r"
  91. for bill in self.bills:
  92. bills_table.add_row([bill.get('desc'), bill.get('amt').quantize(TWOPLACES)])
  93. bills_table.add_row(["----------", "-----"])
  94. bills_table.add_row(["Total", self.total_bills.quantize(TWOPLACES)])
  95. print(bills_table)
  96. print("\nExpenses")
  97. print("***************************")
  98. expenses_table = PrettyTable(["Description", "Amount"])
  99. expenses_table.align["Amount"] = "r"
  100. for expense in self.expenses:
  101. expenses_table.add_row([expense.get('desc'), expense.get('amt').quantize(TWOPLACES)])
  102. expenses_table.add_row(["----------", "-----"])
  103. expenses_table.add_row(["Total", self.total_expenses.quantize(TWOPLACES)])
  104. print(expenses_table)
  105. print("\nTotals")
  106. print("***************************")
  107. print("Expenses Total: " + str(self.total_expenses.quantize(TWOPLACES)))
  108. print("Money After Bills + Expenses: " + str(self.money_left.quantize(TWOPLACES)))
  109. next_period = self.next_pay_period()
  110. money_left_next = next_period.money_left
  111. print("Money After Bills + Expenses Next: " + str(money_left_next.quantize(TWOPLACES)))
  112. if money_left_next <= 200:
  113. print("Not enough money left next paycheck!! Expenses being rolled together!")
  114. self.expenses += next_period.expenses
  115. self.calculate_budget()
  116. self.money_left = self.money_left + money_left_next + next_period.total_expenses
  117. print("Money left after combining: " + str(self.money_left.quantize(TWOPLACES)))
  118. print("\nPercent Expenses")
  119. print("***************************")
  120. total_perc_expenses = Decimal(0)
  121. perc_expenses = PrettyTable(["Description", "Amount"])
  122. perc_expenses.align["Amount"] = "r"
  123. for perc_expense in DATA.get("perc_expenses", []):
  124. amt = perc_expense.get('perc') * self.money_left
  125. total_perc_expenses += amt
  126. perc_expenses.add_row([perc_expense.get('desc'), amt.quantize(TWOPLACES)])
  127. perc_expenses.add_row(["----------", "-----"])
  128. perc_expenses.add_row(["Total", total_perc_expenses.quantize(TWOPLACES)])
  129. print(perc_expenses)
  130. money_left_perc = self.money_left - total_perc_expenses
  131. print("\nAllowances")
  132. print("***************************")
  133. allowance_table = PrettyTable(["Description", "Amount"])
  134. allowance_table.align["Amount"] = "r"
  135. for allowance in DATA.get("allowances", []):
  136. amt = allowance.get('perc') * money_left_perc
  137. allowance_table.add_row([allowance.get('desc'), amt.quantize(TWOPLACES)])
  138. print(allowance_table)
  139. pay_period = PayPeriod()
  140. pay_period.print_budget()