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:
parent
f192d250e7
commit
948d3a2d14
4 changed files with 38 additions and 6 deletions
|
@ -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')),
|
||||
)
|
||||
|
||||
|
||||
|
|
2
setup.py
2
setup.py
|
@ -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+',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in a new issue