@@ -106,6 +106,7 @@ from ..beancount_types import (
Transaction,
)
import odf.element # type:ignore[import]
import odf.style # type:ignore[import]
import odf.table # type:ignore[import]
import rt
@@ -236,6 +237,13 @@ class BaseReport:
class AgingODS(core.BaseODS[AccrualPostings, data.Account]):
AGE_COLORS = [
'#ff00ff',
'#ff0000',
'#ff8800',
'#ffff00',
'#00ff00',
]
DOC_COLUMNS = [
'rt-id',
'invoice',
@@ -283,16 +291,22 @@ class AgingODS(core.BaseODS[AccrualPostings, data.Account]):
self.lock_first_row()
def start_section(self, key: data.Account) -> None:
self.norm_func = core.normalize_amount_func(key)
self.age_thresholds = list(AccrualAccount.by_account(key).value.aging_thresholds)
accrual_type = AccrualAccount.by_account(key)
self.norm_func = accrual_type.normalize_amount
self.age_thresholds = list(accrual_type.value.aging_thresholds)
self.age_thresholds.append(-sys.maxsize)
self.age_balances = [core.MutableBalance() for _ in self.age_thresholds]
accrual_date = self.date - datetime.timedelta(days=self.age_thresholds[-1])
self.age_styles = [
self.merge_styles(self.style_date, self.border_style(
core.Border.LEFT, '10pt', 'solid', color,
)) for color in self.AGE_COLORS
acct_parts = key.slice_parts()
self.use_sheet(acct_parts[1])
self.add_row()
self.add_row(self.string_cell(
f"{' '.join(acct_parts[2:])} {acct_parts[1]} Aging Report"
f" Accrued by {accrual_date.isoformat()} Unpaid by {self.date.isoformat()}",
f" for {self.date.isoformat()}",
stylename=self.merge_styles(self.style_bold, self.style_centertext),
numbercolumnsspanned=self.COL_COUNT,
))
@@ -300,11 +314,12 @@ class AgingODS(core.BaseODS[AccrualPostings, data.Account]):
def end_section(self, key: data.Account) -> None:
total_balance = core.MutableBalance()
text_style = self.merge_styles(self.style_bold, self.style_endtext)
text_span = 4
last_age_text: Optional[str] = None
for threshold, balance in zip(self.age_thresholds, self.age_balances):
for threshold, balance, style in zip(
self.age_thresholds, self.age_balances, self.age_styles,
):
years, days = divmod(threshold, 365)
years_text = f"{years} {'Year' if years == 1 else 'Years'}"
days_text = f"{days} Days"
@@ -316,12 +331,23 @@ class AgingODS(core.BaseODS[AccrualPostings, data.Account]):
age_text = days_text
if last_age_text is None:
age_range = f"Over {age_text}"
elif threshold < 0:
self.add_row(
self.string_cell(
f"Total Unpaid Over {last_age_text}: ",
stylename=self.merge_styles(self.style_bold, self.style_endtext),
numbercolumnsspanned=text_span,
),
*(odf.table.TableCell() for _ in range(1, text_span)),
self.balance_cell(total_balance),
age_range = f"Under {last_age_text}"
else:
age_range = f"{age_text}–{last_age_text}"
f"Total Aged {age_range}: ",
stylename=text_style,
stylename=self.merge_styles(self.style_bold, self.style_endtext, style),
@@ -332,7 +358,7 @@ class AgingODS(core.BaseODS[AccrualPostings, data.Account]):
"Total Unpaid: ",
@@ -343,13 +369,13 @@ class AgingODS(core.BaseODS[AccrualPostings, data.Account]):
row_date = row[0].meta.date
row_balance = self.norm_func(row.balance_at_cost())
age = (self.date - row_date).days
if row_balance.ge_zero():
for index, threshold in enumerate(self.age_thresholds):
if age >= threshold:
self.age_balances[index] += row_balance
break
return
raw_balance = self.norm_func(row.balance())
if raw_balance == row_balance:
amount_cell = odf.table.TableCell()
@@ -360,7 +386,7 @@ class AgingODS(core.BaseODS[AccrualPostings, data.Account]):
projects = row.meta_values('project')
projects.discard(None)
self.date_cell(row_date),
self.date_cell(row_date, stylename=self.age_styles[index]),
self.multiline_cell(sorted(entities)),
amount_cell,
self.balance_cell(row_balance),