Last active
January 9, 2024 07:50
-
-
Save hsiuhsiu/efdae5e2ae9333ce197a38e6a994eb1b to your computer and use it in GitHub Desktop.
sort bean count entries by time
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Sort Beancount Entries by meta['time'] | |
Although beancount says that time is meaningless, there are situations | |
where an account that should not be negative may have a negative balance | |
at some point because transfers that occurred on the same day are | |
scheduled later and spending records are scheduled earlier. | |
This is not a big deal, but it is a bit odd. | |
2021-10-01 balance Assets:Cash 500 USD | |
2021-10-01 * | |
Assets:Cash -800 USD | |
Expenses:Blah | |
2021-10-01 * "trans" | |
Assets:Cash 1000 USD | |
Assets:Bank:Blah | |
Then the balance of Assets:Cash after first transaction will be -300 USD. | |
Now you can add a meta_data named 'time' to fix it: | |
2021-10-01 * | |
time: "18:18:18" | |
Assets:Cash -800 USD | |
Expenses:Blah | |
2021-10-01 * "trans" | |
time: "12:12:12" | |
Assets:Cash 1000 USD | |
Assets:Bank:Blah | |
Then the cash balance changes will be: 500 -> 1500 -> 700 | |
Just active as plugin in <your-main>.bean file. | |
""" | |
from beancount.core import data | |
from datetime import datetime | |
import pytz | |
def __parse_time(time_str): | |
# Define possible formats with floating-point seconds and optional timezone | |
formats = ['%H:%M:%S.%f%z', '%H:%M:%S.%f', '%H:%M:%S%z', '%H:%M:%S'] | |
# Check for timezone and floating-point seconds | |
has_timezone = '+' in time_str or '-' in time_str | |
has_fraction = '.' in time_str | |
# Select the appropriate format | |
if has_timezone and has_fraction: | |
fmt = formats[0] | |
elif has_fraction: | |
fmt = formats[1] | |
elif has_timezone: | |
fmt = formats[2] | |
else: | |
fmt = formats[3] | |
dt = datetime.strptime(time_str, fmt) | |
# If the datetime object is not timezone-aware, attach the default timezone | |
if dt.tzinfo is None: | |
dt = pytz.UTC.localize(dt) | |
return dt | |
__original_entry_sortkey = data.entry_sortkey | |
__default_time_value = __parse_time('12:00:00') | |
def __entry_sortkey(entry): | |
original_result = __original_entry_sortkey(entry) | |
time_meta = entry.meta.get( | |
"time", None) if entry.meta is not None else None | |
time_value = __parse_time( | |
time_meta) if time_meta is not None else __default_time_value | |
return original_result[0], original_result[1], time_value, original_result[2] | |
def __posting_sortkey(entry): | |
if isinstance(entry, data.TxnPosting): | |
entry = entry.txn | |
return __entry_sortkey(entry) | |
data.entry_sortkey = __entry_sortkey | |
data.posting_sortkey = __posting_sortkey |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment