accrual: Add columns to the aging report. RT#11439.

This adds almost all the metadata that's relevant to accruals.
I considered adding statement, but that cuased rows to get spaced out a lot,
and statement's kind of a low-value column, so I decided against it.

Ultimately I would like to make this configurable but that's for the
future.
This commit is contained in:
Brett Smith 2020-06-09 15:56:59 -04:00
parent f192d250e7
commit 948d3a2d14
4 changed files with 38 additions and 6 deletions

View file

@ -334,8 +334,12 @@ class AgingODS(core.BaseODS[AccrualPostings, Optional[data.Account]]):
'Entity',
'Invoice Amount',
'Booked Amount',
'Project',
'Ticket',
'Invoice',
'Approval',
'Contract',
'Purchase Order',
]
COL_COUNT = len(COLUMNS)
@ -402,7 +406,7 @@ class AgingODS(core.BaseODS[AccrualPostings, Optional[data.Account]]):
return
total_balance = core.MutableBalance()
text_style = self.merge_styles(self.style_bold, self.style_endtext)
text_span = self.COL_COUNT - 1
text_span = 4
last_age_text: Optional[str] = None
self.add_row()
for threshold, balance in zip(self.age_thresholds, self.age_balances):
@ -474,13 +478,19 @@ class AgingODS(core.BaseODS[AccrualPostings, Optional[data.Account]]):
amount_cell = odf.table.TableCell()
else:
amount_cell = self.balance_cell(raw_balance)
projects = {post.meta.get('project') or None for post in row}
projects.discard(None)
self.add_row(
self.date_cell(row[0].meta.date),
self.multiline_cell(row.entities()),
amount_cell,
self.balance_cell(row.end_balance),
self.multiline_cell(sorted(projects)),
self.multilink_cell(self._link_seq(row, 'rt-id')),
self.multilink_cell(self._link_seq(row, 'invoice')),
self.multilink_cell(self._link_seq(row, 'approval')),
self.multilink_cell(self._link_seq(row, 'contract')),
self.multilink_cell(self._link_seq(row, 'purchase-order')),
)

View file

@ -5,7 +5,7 @@ from setuptools import setup
setup(
name='conservancy_beancount',
description="Plugin, library, and reports for reading Conservancy's books",
version='1.1.5',
version='1.1.6',
author='Software Freedom Conservancy',
author_email='info@sfconservancy.org',
license='GNU AGPLv3+',

View file

@ -15,36 +15,42 @@
2010-03-05 * "EarlyBird" "Payment for receivable from previous FY"
rt-id: "rt:40"
invoice: "rt:40/400"
project: "Conservancy"
Assets:Receivable:Accounts -500 USD
Assets:Checking 500 USD
2010-03-06 * "EarlyBird" "Payment for payment from previous FY"
rt-id: "rt:44"
invoice: "rt:44/440"
project: "Conservancy"
Liabilities:Payable:Accounts 125 USD
Assets:Checking -125 USD
2010-03-15 * "GrantCo" "2010Q1 grant"
rt-id: "rt:470"
invoice: "rt:470/4700"
project: "Development Grant"
Assets:Receivable:Accounts 5000 USD
Income:Donations -5000 USD
2010-03-25 * "GrantCo" "2010Q1 grant ACH payment"
rt-id: "rt:470"
invoice: "rt:470/4700"
project: "Development Grant"
Assets:Receivable:Accounts -5000 USD
Assets:Checking 5000 USD
2010-03-30 * "EarlyBird" "Travel reimbursement"
rt-id: "rt:490"
invoice: "rt:490/4900"
project: "Conservancy"
Liabilities:Payable:Accounts -75 USD
Expenses:Travel 75 USD
2010-04-15 * "Multiparty invoice"
rt-id: "rt:480"
invoice: "rt:480/4800"
project: "Conservancy"
Expenses:Travel 250 USD
Liabilities:Payable:Accounts -125 USD
entity: "MultiPartyA"
@ -54,12 +60,14 @@
2010-04-18 * "MultiPartyA" "Payment for 480"
rt-id: "rt:480"
invoice: "rt:480/4800"
project: "Conservancy"
Liabilities:Payable:Accounts 125 USD
Assets:Checking -125 USD
2010-04-20 * "MultiPartyB" "Payment for 480"
rt-id: "rt:480"
invoice: "rt:480/4800"
project: "Conservancy"
Liabilities:Payable:Accounts 125 USD
Assets:Checking -125 USD
@ -67,6 +75,7 @@
rt-id: "rt:310"
contract: "rt:310/3100"
invoice: "FIXME" ; still waiting on them to send it
project: "Conservancy"
Liabilities:Payable:Accounts -200 USD
Expenses:Travel 200 USD
@ -74,6 +83,7 @@
rt-id: "rt:505"
invoice: "rt:505/5050"
approval: "rt:505/5040"
project: "Conservancy"
Income:Donations -2,500 EUR {1.100 USD}
Assets:Receivable:Accounts 2,500 EUR {1.100 USD}
@ -81,18 +91,21 @@
rt-id: "rt:510"
invoice: "rt:510/5100"
contract: "rt:510/4000"
project: "Conservancy"
Expenses:Services:Legal 200.00 USD
Liabilities:Payable:Accounts -200.00 USD
2010-05-15 * "MatchingProgram" "May matched donations"
invoice: "rt://ticket/515/attachments/5150"
approval: "rt://ticket/515/attachments/5140"
project: "Conservancy"
Income:Donations -1500.00 USD
Assets:Receivable:Accounts 1500.00 USD
2010-05-20 * "DonorA" "Donation made"
rt-id: "rt:505"
invoice: "rt:505/5050"
project: "Conservancy"
Assets:Receivable:Accounts -2,750.00 USD
Assets:Checking 2,750.00 USD
receipt: "DonorAWire.pdf"
@ -100,6 +113,7 @@
2010-05-25 * "Lawyer" "May payment"
rt-id: "rt:510"
invoice: "rt:510/5100"
project: "Conservancy"
Liabilities:Payable:Accounts 200.00 USD
contract: "rt:510/4000"
Assets:Checking -200.00 USD
@ -109,6 +123,7 @@
rt-id: "rt:510"
invoice: "rt:510/6100"
contract: "rt:510/4000"
project: "Conservancy"
Expenses:Services:Legal 220.00 USD
Liabilities:Payable:Accounts -220.00 USD
@ -116,6 +131,7 @@
rt-id: "rt:510"
invoice: "rt:510/6100"
contract: "rt:510/4000"
project: "Conservancy"
Expenses:FilingFees 60.00 USD
Liabilities:Payable:Accounts -60.00 USD
@ -125,11 +141,13 @@
rt-id: "rt:520 rt:525"
invoice: "rt:520/5200"
contract: "rt:520/5220"
project: "Conservancy"
Liabilities:Payable:Accounts -1,000 EUR {1.100 USD}
Expenses:FilingFees 1,000 EUR {1.100 USD}
2010-06-15 * "GrantCo" "2010Q2 grant"
rt-id: "rt:470"
invoice: "rt:470/4700"
project: "Development Grant"
Assets:Receivable:Accounts 5500 USD
Income:Donations -5500 USD

View file

@ -75,20 +75,22 @@ class AgingRow(NamedTuple):
at_cost: bc_data.Amount
rt_id: Sequence[str]
invoice: Sequence[str]
project: Sequence[str]
@classmethod
def make_simple(cls, date, entity, at_cost, invoice, rt_id=None, orig_amount=None):
def make_simple(cls, date, entity, at_cost, invoice,
rt_id=None, orig_amount=None, project='Conservancy'):
if isinstance(date, str):
date = datetime.datetime.strptime(date, '%Y-%m-%d').date()
if not isinstance(at_cost, tuple):
at_cost = testutil.Amount(at_cost)
if rt_id is None:
rt_id, _, _ = invoice.partition('/')
return cls(date, [entity], orig_amount, at_cost, [rt_id], [invoice])
return cls(date, [entity], orig_amount, at_cost, [rt_id], [invoice], [project])
def check_row_match(self, sheet_row):
cells = testutil.ODSCell.from_row(sheet_row)
assert len(cells) == len(self)
assert len(cells) >= len(self)
cells = iter(cells)
assert next(cells).value == self.date
assert next(cells).text == '\0'.join(self.entity)
@ -99,6 +101,7 @@ class AgingRow(NamedTuple):
usd_cell = next(cells)
assert usd_cell.value_type == 'currency'
assert usd_cell.value == self.at_cost.number
assert next(cells).text == '\0'.join(self.project)
for index, cell in enumerate(cells):
links = cell.getElementsByType(odf.text.A)
assert len(links) == len(cell.childNodes)
@ -118,7 +121,8 @@ AGING_AR = [
AgingRow.make_simple('2010-03-05', 'EarlyBird', -500, 'rt:40/400'),
AgingRow.make_simple('2010-05-15', 'MatchingProgram', 1500,
'rt://ticket/515/attachments/5150'),
AgingRow.make_simple('2010-06-15', 'GrantCo', 5500, 'rt:470/4700'),
AgingRow.make_simple('2010-06-15', 'GrantCo', 5500, 'rt:470/4700',
project='Development Grant'),
]
class RTClient(testutil.RTClient):