Splits income into fixed and extra
This will provide differentiation between income that's regular and stable (long-term contract) and that which is not, such as gigs, presents, etc. It presents the information separated in both graphs and reports.
This commit is contained in:
parent
a1f5699b12
commit
59406c35c1
@ -1,29 +1,38 @@
|
|||||||
Groups:
|
Groups:
|
||||||
|
income-fixed:
|
||||||
|
color: limegreen
|
||||||
|
linestyle: dashed
|
||||||
|
income-extra:
|
||||||
|
color: limegreen
|
||||||
|
linestyle: dashed
|
||||||
income:
|
income:
|
||||||
color: limegreen
|
color: limegreen
|
||||||
|
linestyle: solid
|
||||||
fixed:
|
fixed:
|
||||||
color: tab:blue
|
color: tab:blue
|
||||||
required:
|
required:
|
||||||
color: tab:orange
|
color: tab:orange
|
||||||
health:
|
health:
|
||||||
color: tab:red
|
color: tab:red
|
||||||
|
travel:
|
||||||
|
color: tab:cyan
|
||||||
discretionary:
|
discretionary:
|
||||||
color: tab:brown
|
color: tab:brown
|
||||||
|
|
||||||
Income1:
|
Income1:
|
||||||
group: income
|
group: income-fixed
|
||||||
regex:
|
regex:
|
||||||
- company A
|
- company A
|
||||||
|
|
||||||
Income2:
|
Income2:
|
||||||
group: income
|
group: income-fixed
|
||||||
regex:
|
regex:
|
||||||
- transfer
|
- transfer
|
||||||
banks:
|
banks:
|
||||||
- BankA
|
- BankA
|
||||||
|
|
||||||
Income3:
|
Income3:
|
||||||
group: income
|
group: income-extra
|
||||||
regex:
|
regex:
|
||||||
- company B
|
- company B
|
||||||
|
|
||||||
@ -76,13 +85,14 @@ Medical:
|
|||||||
Miscellaneous:
|
Miscellaneous:
|
||||||
|
|
||||||
Travel:
|
Travel:
|
||||||
group: discretionary
|
group: travel
|
||||||
regex:
|
regex:
|
||||||
- ryanair
|
- ryanair
|
||||||
- easyjet
|
- easyjet
|
||||||
- airbnb
|
- airbnb
|
||||||
not_in_groups:
|
not_in_groups:
|
||||||
- income
|
- income-fixed
|
||||||
|
- income-extra
|
||||||
- fixed
|
- fixed
|
||||||
date_fmt: "%Y-%m-%d"
|
date_fmt: "%Y-%m-%d"
|
||||||
vacations:
|
vacations:
|
||||||
|
|||||||
@ -30,7 +30,9 @@ Options = namedtuple(
|
|||||||
cfg = yaml.safe_load(open("categories.yaml"))
|
cfg = yaml.safe_load(open("categories.yaml"))
|
||||||
try:
|
try:
|
||||||
categories = {
|
categories = {
|
||||||
k: Options(**v) if v and k != "Groups" else Options() for k, v in cfg.items()
|
str(k): Options(**v) if v else Options()
|
||||||
|
for k, v in cfg.items()
|
||||||
|
if k and k != "Groups"
|
||||||
}
|
}
|
||||||
except TypeError:
|
except TypeError:
|
||||||
logging.exception("Invalid option in categories.yaml")
|
logging.exception("Invalid option in categories.yaml")
|
||||||
@ -129,7 +131,7 @@ def vacations(db: DBManager) -> None:
|
|||||||
db.update_categories(transactions)
|
db.update_categories(transactions)
|
||||||
|
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
print(e)
|
logging.exception(e)
|
||||||
|
|
||||||
|
|
||||||
def nulls(db: DBManager) -> None:
|
def nulls(db: DBManager) -> None:
|
||||||
|
|||||||
@ -47,25 +47,45 @@ def monthly(
|
|||||||
plt.figure(tight_layout=True)
|
plt.figure(tight_layout=True)
|
||||||
plt.plot(
|
plt.plot(
|
||||||
list(rrule(MONTHLY, dtstart=start.replace(day=1), until=end.replace(day=1))),
|
list(rrule(MONTHLY, dtstart=start.replace(day=1), until=end.replace(day=1))),
|
||||||
[groups["income"] for _, groups in monthly_transactions],
|
[
|
||||||
|
sum(
|
||||||
|
value
|
||||||
|
for group, value in groups.items()
|
||||||
|
if group == "income-fixed" or group == "income-extra"
|
||||||
|
)
|
||||||
|
for _, groups in monthly_transactions
|
||||||
|
],
|
||||||
color=groups["income"]["color"],
|
color=groups["income"]["color"],
|
||||||
|
linestyle=groups["income"]["linestyle"],
|
||||||
|
)
|
||||||
|
plt.plot(
|
||||||
|
list(rrule(MONTHLY, dtstart=start.replace(day=1), until=end.replace(day=1))),
|
||||||
|
[groups["income-fixed"] for _, groups in monthly_transactions],
|
||||||
|
color=groups["income-fixed"]["color"],
|
||||||
|
linestyle=groups["income-fixed"]["linestyle"],
|
||||||
)
|
)
|
||||||
plt.stackplot(
|
plt.stackplot(
|
||||||
list(rrule(MONTHLY, dtstart=start.replace(day=1), until=end.replace(day=1))),
|
list(rrule(MONTHLY, dtstart=start.replace(day=1), until=end.replace(day=1))),
|
||||||
[
|
[
|
||||||
[-groups[group] for _, groups in monthly_transactions]
|
[-groups[group] for _, groups in monthly_transactions]
|
||||||
for group in pfbudget.categories.groups
|
for group in pfbudget.categories.groups
|
||||||
if group != "income" and group != "investment"
|
if group != "income-fixed"
|
||||||
|
and group != "income-extra"
|
||||||
|
and group != "investment"
|
||||||
],
|
],
|
||||||
labels=[
|
labels=[
|
||||||
group
|
group
|
||||||
for group in pfbudget.categories.groups
|
for group in pfbudget.categories.groups
|
||||||
if group != "income" and group != "investment"
|
if group != "income-fixed"
|
||||||
|
and group != "income-extra"
|
||||||
|
and group != "investment"
|
||||||
],
|
],
|
||||||
colors=[
|
colors=[
|
||||||
groups.get(group, {"color": "gray"})["color"]
|
groups.get(group, {"color": "gray"})["color"]
|
||||||
for group in pfbudget.categories.groups
|
for group in pfbudget.categories.groups
|
||||||
if group != "income" and group != "investment"
|
if group != "income-fixed"
|
||||||
|
and group != "income-extra"
|
||||||
|
and group != "investment"
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
plt.legend(loc="upper left")
|
plt.legend(loc="upper left")
|
||||||
@ -111,25 +131,42 @@ def discrete(
|
|||||||
sum(
|
sum(
|
||||||
value
|
value
|
||||||
for category, value in categories.items()
|
for category, value in categories.items()
|
||||||
if category in pfbudget.categories.groups["income"]
|
if category in pfbudget.categories.groups["income-fixed"]
|
||||||
|
or category in pfbudget.categories.groups["income-extra"]
|
||||||
)
|
)
|
||||||
for _, categories in monthly_transactions
|
for _, categories in monthly_transactions
|
||||||
],
|
],
|
||||||
color=groups["income"]["color"],
|
color=groups["income"]["color"],
|
||||||
|
linestyle=groups["income"]["linestyle"],
|
||||||
|
)
|
||||||
|
plt.plot(
|
||||||
|
list(rrule(MONTHLY, dtstart=start.replace(day=1), until=end.replace(day=1))),
|
||||||
|
[
|
||||||
|
sum(
|
||||||
|
value
|
||||||
|
for category, value in categories.items()
|
||||||
|
if category in pfbudget.categories.groups["income-fixed"]
|
||||||
|
)
|
||||||
|
for _, categories in monthly_transactions
|
||||||
|
],
|
||||||
|
color=groups["income-fixed"]["color"],
|
||||||
|
linestyle=groups["income-fixed"]["linestyle"],
|
||||||
)
|
)
|
||||||
plt.stackplot(
|
plt.stackplot(
|
||||||
list(rrule(MONTHLY, dtstart=start.replace(day=1), until=end.replace(day=1))),
|
list(rrule(MONTHLY, dtstart=start.replace(day=1), until=end.replace(day=1))),
|
||||||
[
|
[
|
||||||
[-categories[category] for _, categories in monthly_transactions]
|
[-categories[category] for _, categories in monthly_transactions]
|
||||||
for category in pfbudget.categories.categories
|
for category in pfbudget.categories.categories
|
||||||
if category not in pfbudget.categories.groups["income"]
|
if category not in pfbudget.categories.groups["income-fixed"]
|
||||||
|
and category not in pfbudget.categories.groups["income-extra"]
|
||||||
and category not in pfbudget.categories.groups["investment"]
|
and category not in pfbudget.categories.groups["investment"]
|
||||||
and category != "Null"
|
and category != "Null"
|
||||||
],
|
],
|
||||||
labels=[
|
labels=[
|
||||||
category
|
category
|
||||||
for category in pfbudget.categories.categories
|
for category in pfbudget.categories.categories
|
||||||
if category not in pfbudget.categories.groups["income"]
|
if category not in pfbudget.categories.groups["income-fixed"]
|
||||||
|
and category not in pfbudget.categories.groups["income-extra"]
|
||||||
and category not in pfbudget.categories.groups["investment"]
|
and category not in pfbudget.categories.groups["investment"]
|
||||||
and category != "Null"
|
and category != "Null"
|
||||||
],
|
],
|
||||||
|
|||||||
@ -3,7 +3,7 @@ from dateutil.rrule import rrule, YEARLY
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
|
|
||||||
import pfbudget.categories as categories
|
import pfbudget.categories
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from pfbudget.database import DBManager
|
from pfbudget.database import DBManager
|
||||||
@ -23,7 +23,7 @@ def net(db: DBManager, start: dt.date = dt.date.min, end: dt.date = dt.date.max)
|
|||||||
if transaction.category in categories
|
if transaction.category in categories
|
||||||
and year <= transaction.date <= year.replace(month=12, day=31)
|
and year <= transaction.date <= year.replace(month=12, day=31)
|
||||||
)
|
)
|
||||||
for group, categories in categories.groups.items()
|
for group, categories in pfbudget.categories.groups.items()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
for year in [
|
for year in [
|
||||||
@ -35,8 +35,29 @@ def net(db: DBManager, start: dt.date = dt.date.min, end: dt.date = dt.date.max)
|
|||||||
)
|
)
|
||||||
|
|
||||||
for year, groups in yearly_transactions:
|
for year, groups in yearly_transactions:
|
||||||
print(year.year)
|
print(f"\n{year.year}\n")
|
||||||
print(f"Income: {groups.pop('income'):.2f}€")
|
|
||||||
|
income = groups.pop("income-fixed") + groups.pop("income-extra")
|
||||||
|
print(f"Income: {income:.2f} €\n")
|
||||||
|
|
||||||
|
investments = -groups.pop("investment")
|
||||||
|
|
||||||
|
expenses = 0
|
||||||
for group, value in groups.items():
|
for group, value in groups.items():
|
||||||
print(f"{group.capitalize()} expenses: {value:.2f}€")
|
expenses -= value
|
||||||
print()
|
if income != 0:
|
||||||
|
print(
|
||||||
|
f"{group.capitalize()} expenses: {-value:.2f} € ({-value/income*100:.1f}%)"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(f"{group.capitalize()} expenses: {-value:.2f} €")
|
||||||
|
|
||||||
|
print(f"\nNet total: {income-expenses:.2f} €")
|
||||||
|
if income != 0:
|
||||||
|
print(
|
||||||
|
f"Total expenses are {expenses:.2f} ({expenses/income*100:.1f}% of income)\n"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(f"Total expenses are {expenses:.2f}. No income this year!\n")
|
||||||
|
|
||||||
|
print(f"Invested: {investments:.2f}€\n")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user