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