reports: Add RelatedPostings.balance_at_cost() method.
This makes it easy to get results similar to `ledger -V`.
This commit is contained in:
parent
52fa66bba1
commit
81d216f282
2 changed files with 65 additions and 0 deletions
|
@ -199,6 +199,16 @@ class RelatedPostings(Sequence[data.Posting]):
|
||||||
except NameError:
|
except NameError:
|
||||||
return Balance()
|
return Balance()
|
||||||
|
|
||||||
|
def balance_at_cost(self) -> Balance:
|
||||||
|
balance = MutableBalance()
|
||||||
|
for post in self:
|
||||||
|
if post.cost is None:
|
||||||
|
balance.add_amount(post.units)
|
||||||
|
else:
|
||||||
|
number = post.units.number * post.cost.number
|
||||||
|
balance.add_amount(data.Amount(number, post.cost.currency))
|
||||||
|
return balance
|
||||||
|
|
||||||
def meta_values(self,
|
def meta_values(self,
|
||||||
key: MetaKey,
|
key: MetaKey,
|
||||||
default: Optional[MetaValue]=None,
|
default: Optional[MetaValue]=None,
|
||||||
|
|
|
@ -135,6 +135,61 @@ def test_iter_with_balance_credit_card(credit_card_cycle):
|
||||||
def test_iter_with_balance_two_acccruals(two_accruals_three_payments):
|
def test_iter_with_balance_two_acccruals(two_accruals_three_payments):
|
||||||
check_iter_with_balance(two_accruals_three_payments)
|
check_iter_with_balance(two_accruals_three_payments)
|
||||||
|
|
||||||
|
def test_balance_at_cost_mixed():
|
||||||
|
txn = testutil.Transaction(postings=[
|
||||||
|
('Expenses:Other', '22'),
|
||||||
|
('Expenses:Other', '30', 'EUR', ('1.1',)),
|
||||||
|
('Expenses:Other', '40', 'EUR'),
|
||||||
|
('Expenses:Other', '50', 'USD', ('1.1', 'EUR')),
|
||||||
|
])
|
||||||
|
related = core.RelatedPostings(data.Posting.from_txn(txn))
|
||||||
|
balance = related.balance_at_cost()
|
||||||
|
amounts = set(balance.values())
|
||||||
|
assert amounts == {testutil.Amount(55, 'USD'), testutil.Amount(95, 'EUR')}
|
||||||
|
|
||||||
|
def test_balance_at_single_currency_cost():
|
||||||
|
txn = testutil.Transaction(postings=[
|
||||||
|
('Expenses:Other', '22'),
|
||||||
|
('Expenses:Other', '30', 'EUR', ('1.1',)),
|
||||||
|
('Expenses:Other', '40', 'GBP', ('1.1',)),
|
||||||
|
])
|
||||||
|
related = core.RelatedPostings(data.Posting.from_txn(txn))
|
||||||
|
balance = related.balance_at_cost()
|
||||||
|
amounts = set(balance.values())
|
||||||
|
assert amounts == {testutil.Amount(99)}
|
||||||
|
|
||||||
|
def test_balance_at_cost_zeroed_out():
|
||||||
|
txn = testutil.Transaction(postings=[
|
||||||
|
('Income:Other', '-22'),
|
||||||
|
('Assets:Receivable:Accounts', '20', 'EUR', ('1.1',)),
|
||||||
|
])
|
||||||
|
related = core.RelatedPostings(data.Posting.from_txn(txn))
|
||||||
|
balance = related.balance_at_cost()
|
||||||
|
assert balance.is_zero()
|
||||||
|
|
||||||
|
def test_balance_at_cost_singleton():
|
||||||
|
txn = testutil.Transaction(postings=[
|
||||||
|
('Assets:Receivable:Accounts', '20', 'EUR', ('1.1',)),
|
||||||
|
])
|
||||||
|
related = core.RelatedPostings(data.Posting.from_txn(txn))
|
||||||
|
balance = related.balance_at_cost()
|
||||||
|
amounts = set(balance.values())
|
||||||
|
assert amounts == {testutil.Amount(22)}
|
||||||
|
|
||||||
|
def test_balance_at_cost_singleton_without_cost():
|
||||||
|
txn = testutil.Transaction(postings=[
|
||||||
|
('Assets:Receivable:Accounts', '20'),
|
||||||
|
])
|
||||||
|
related = core.RelatedPostings(data.Posting.from_txn(txn))
|
||||||
|
balance = related.balance_at_cost()
|
||||||
|
amounts = set(balance.values())
|
||||||
|
assert amounts == {testutil.Amount(20)}
|
||||||
|
|
||||||
|
def test_balance_at_cost_empty():
|
||||||
|
related = core.RelatedPostings()
|
||||||
|
balance = related.balance_at_cost()
|
||||||
|
assert balance.is_zero()
|
||||||
|
|
||||||
def test_meta_values_empty():
|
def test_meta_values_empty():
|
||||||
related = core.RelatedPostings()
|
related = core.RelatedPostings()
|
||||||
assert related.meta_values('key') == set()
|
assert related.meta_values('key') == set()
|
||||||
|
|
Loading…
Reference in a new issue