108 lines
3.1 KiB
Plaintext
108 lines
3.1 KiB
Plaintext
|
|
#!/usr/bin/env python3
|
||
|
|
from beancount import loader
|
||
|
|
from beancount.query import query
|
||
|
|
from beancount.core.data import Transaction
|
||
|
|
from beancount.parser import printer
|
||
|
|
import argparse
|
||
|
|
from datetime import date, timedelta
|
||
|
|
from tabulate import tabulate
|
||
|
|
from collections import defaultdict
|
||
|
|
|
||
|
|
|
||
|
|
class bcolors:
|
||
|
|
HEADER = '\033[95m'
|
||
|
|
OKBLUE = '\033[94m'
|
||
|
|
OKCYAN = '\033[96m'
|
||
|
|
OKGREEN = '\033[92m'
|
||
|
|
WARNING = '\033[93m'
|
||
|
|
FAIL = '\033[91m'
|
||
|
|
ENDC = '\033[0m'
|
||
|
|
BOLD = '\033[1m'
|
||
|
|
UNDERLINE = '\033[4m'
|
||
|
|
|
||
|
|
|
||
|
|
def get_shared_expenses(entries, options, start_date, end_date):
|
||
|
|
expenses_query = f"""SELECT date, account, value(position) FROM
|
||
|
|
date >= {start_date} AND
|
||
|
|
date < {end_date.isoformat()}
|
||
|
|
WHERE account ~ \"Expenses:Lloguer\" OR account ~ \"Expenses:FacturesUtilitats\""""
|
||
|
|
rtypes, rrows = query.run_query(
|
||
|
|
entries, options, expenses_query)
|
||
|
|
expenses = []
|
||
|
|
for row in rrows:
|
||
|
|
if row.value_position.number > 0:
|
||
|
|
expenses.append(row)
|
||
|
|
return expenses
|
||
|
|
|
||
|
|
|
||
|
|
def get_paid_equities(entries, options, start_date, end_date):
|
||
|
|
expenses_query = f"""SELECT date, account, value(position) FROM
|
||
|
|
date >= {start_date} AND
|
||
|
|
date < {end_date.isoformat()}
|
||
|
|
WHERE account ~ \"Equity:LloguerMiquel\" OR account ~ \"Equity:FacturesUtilitatsMiquel\""""
|
||
|
|
rtypes, rrows = query.run_query(
|
||
|
|
entries, options, expenses_query)
|
||
|
|
return rrows
|
||
|
|
|
||
|
|
|
||
|
|
def find_matching_expense(expense, equities):
|
||
|
|
for equity in equities:
|
||
|
|
if equity.value_position == expense.value_position:
|
||
|
|
return True
|
||
|
|
return False
|
||
|
|
|
||
|
|
|
||
|
|
def find_missing_shared_expenses(expenses, equities):
|
||
|
|
missing_shared_expenses = []
|
||
|
|
for expense in expenses:
|
||
|
|
if not find_matching_expense(expense, equities):
|
||
|
|
missing_shared_expenses.append(expense)
|
||
|
|
return missing_shared_expenses
|
||
|
|
|
||
|
|
|
||
|
|
def print_report(missing_shared_expenses, start_date):
|
||
|
|
print("Missing shared expenses")
|
||
|
|
print(f"From {start_date}")
|
||
|
|
print(missing_shared_expenses)
|
||
|
|
report = {
|
||
|
|
"Date": [],
|
||
|
|
"Account": [],
|
||
|
|
"Amount": []
|
||
|
|
}
|
||
|
|
for expense in missing_shared_expenses:
|
||
|
|
report["Date"].append(expense.date)
|
||
|
|
report["Account"].append(expense.account)
|
||
|
|
report["Amount"].append(expense.value_position)
|
||
|
|
print(tabulate(report))
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
parser = argparse.ArgumentParser(
|
||
|
|
description='Find pending shared expenses (checks last 12 months)')
|
||
|
|
args = parser.parse_args()
|
||
|
|
|
||
|
|
# Calculate date range: 1 year ago to today
|
||
|
|
end_date = date.today()
|
||
|
|
start_date = end_date - timedelta(days=365)
|
||
|
|
|
||
|
|
print(f"Checking period: {start_date.isoformat()} to {
|
||
|
|
end_date.isoformat()}")
|
||
|
|
|
||
|
|
filename = "ledger/main.beancount"
|
||
|
|
entries, errors, options = loader.load_file(filename)
|
||
|
|
|
||
|
|
if errors:
|
||
|
|
printer.print_errors(errors)
|
||
|
|
return
|
||
|
|
|
||
|
|
expenses = get_shared_expenses(entries, options, start_date, end_date)
|
||
|
|
equities = get_paid_equities(entries, options, start_date, end_date)
|
||
|
|
|
||
|
|
missing_shared_expenses = find_missing_shared_expenses(expenses, equities)
|
||
|
|
|
||
|
|
print_report(missing_shared_expenses, start_date)
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|