accruals: AccrualPostings only reports inconsistent cost per currency.
Of course if an accrual has multiple currencies, it'll probably have different costs, and that's fine.
This commit is contained in:
parent
3d704e2865
commit
c88c5ef3b0
3 changed files with 15 additions and 35 deletions
|
@ -144,7 +144,6 @@ class AccrualPostings(core.RelatedPostings):
|
||||||
_FIELDS: Dict[str, Callable[[data.Posting], MetaValue]] = {
|
_FIELDS: Dict[str, Callable[[data.Posting], MetaValue]] = {
|
||||||
'account': operator.attrgetter('account'),
|
'account': operator.attrgetter('account'),
|
||||||
'contract': _meta_getter('contract'),
|
'contract': _meta_getter('contract'),
|
||||||
'cost': operator.attrgetter('cost'),
|
|
||||||
'entity': _meta_getter('entity'),
|
'entity': _meta_getter('entity'),
|
||||||
'invoice': _meta_getter('invoice'),
|
'invoice': _meta_getter('invoice'),
|
||||||
'purchase_order': _meta_getter('purchase-order'),
|
'purchase_order': _meta_getter('purchase-order'),
|
||||||
|
@ -157,8 +156,6 @@ class AccrualPostings(core.RelatedPostings):
|
||||||
'accounts',
|
'accounts',
|
||||||
'contract',
|
'contract',
|
||||||
'contracts',
|
'contracts',
|
||||||
'cost',
|
|
||||||
'costs',
|
|
||||||
'entity',
|
'entity',
|
||||||
'entitys',
|
'entitys',
|
||||||
'entities',
|
'entities',
|
||||||
|
@ -226,6 +223,17 @@ class AccrualPostings(core.RelatedPostings):
|
||||||
get_func(post),
|
get_func(post),
|
||||||
)
|
)
|
||||||
yield Error(post.meta, errmsg, post.meta.txn)
|
yield Error(post.meta, errmsg, post.meta.txn)
|
||||||
|
costs = collections.defaultdict(set)
|
||||||
|
for post in self:
|
||||||
|
costs[post.units.currency].add(post.cost)
|
||||||
|
for code, currency_costs in costs.items():
|
||||||
|
if len(currency_costs) > 1:
|
||||||
|
for post in self:
|
||||||
|
if post.units.currency == code:
|
||||||
|
errmsg = 'inconsistent cost for invoice {}: {}'.format(
|
||||||
|
self.invoice or "<none>", post.cost,
|
||||||
|
)
|
||||||
|
yield Error(post.meta, errmsg, post.meta.txn)
|
||||||
|
|
||||||
def is_paid(self, default: Optional[bool]=None) -> Optional[bool]:
|
def is_paid(self, default: Optional[bool]=None) -> Optional[bool]:
|
||||||
if self.accrual_type is None:
|
if self.accrual_type is None:
|
||||||
|
|
|
@ -41,8 +41,8 @@
|
||||||
rt-id: "rt:505"
|
rt-id: "rt:505"
|
||||||
invoice: "rt:505/5050"
|
invoice: "rt:505/5050"
|
||||||
approval: "rt:505/5040"
|
approval: "rt:505/5040"
|
||||||
Income:Donations -1000.00 USD
|
Income:Donations -2,500 EUR {1.100 USD}
|
||||||
Assets:Receivable:Accounts 1000.00 USD
|
Assets:Receivable:Accounts 2,500 EUR {1.100 USD}
|
||||||
|
|
||||||
2020-05-10 * "Lawyer" "April legal services"
|
2020-05-10 * "Lawyer" "April legal services"
|
||||||
rt-id: "rt:510"
|
rt-id: "rt:510"
|
||||||
|
@ -60,8 +60,8 @@
|
||||||
2020-05-20 * "DonorA" "Donation made"
|
2020-05-20 * "DonorA" "Donation made"
|
||||||
rt-id: "rt:505"
|
rt-id: "rt:505"
|
||||||
invoice: "rt:505/5050"
|
invoice: "rt:505/5050"
|
||||||
Assets:Receivable:Accounts -1000.00 USD
|
Assets:Receivable:Accounts -2,750.00 USD
|
||||||
Assets:Checking 1000.00 USD
|
Assets:Checking 2,750.00 USD
|
||||||
receipt: "DonorAWire.pdf"
|
receipt: "DonorAWire.pdf"
|
||||||
|
|
||||||
2020-05-25 * "Lawyer" "May payment"
|
2020-05-25 * "Lawyer" "May payment"
|
||||||
|
|
|
@ -212,20 +212,6 @@ def test_accrual_postings_consistent_account(acct_name):
|
||||||
assert related.account == acct_name
|
assert related.account == acct_name
|
||||||
assert related.accounts == {acct_name}
|
assert related.accounts == {acct_name}
|
||||||
|
|
||||||
@pytest.mark.parametrize('cost', [
|
|
||||||
testutil.Cost('1.2', 'USD'),
|
|
||||||
None,
|
|
||||||
])
|
|
||||||
def test_accrual_postings_consistent_cost(cost):
|
|
||||||
meta = {'invoice': 'FXinvoice.pdf'}
|
|
||||||
txn = testutil.Transaction(postings=[
|
|
||||||
(ACCOUNTS[0], 60, 'EUR', cost, meta),
|
|
||||||
(ACCOUNTS[0], 30, 'EUR', cost, meta),
|
|
||||||
])
|
|
||||||
related = accrual.AccrualPostings(data.Posting.from_txn(txn))
|
|
||||||
assert related.cost == cost
|
|
||||||
assert related.costs == {cost}
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('meta_key,acct_name', testutil.combine_values(
|
@pytest.mark.parametrize('meta_key,acct_name', testutil.combine_values(
|
||||||
CONSISTENT_METADATA,
|
CONSISTENT_METADATA,
|
||||||
ACCOUNTS,
|
ACCOUNTS,
|
||||||
|
@ -255,20 +241,6 @@ def test_accrual_postings_inconsistent_account():
|
||||||
assert related.account is related.INCONSISTENT
|
assert related.account is related.INCONSISTENT
|
||||||
assert related.accounts == set(ACCOUNTS)
|
assert related.accounts == set(ACCOUNTS)
|
||||||
|
|
||||||
def test_accrual_postings_inconsistent_cost():
|
|
||||||
meta = {'invoice': 'FXinvoice.pdf'}
|
|
||||||
costs = {
|
|
||||||
testutil.Cost('1.1', 'USD'),
|
|
||||||
testutil.Cost('1.2', 'USD'),
|
|
||||||
}
|
|
||||||
txn = testutil.Transaction(postings=[
|
|
||||||
(ACCOUNTS[0], 10, 'EUR', cost, meta)
|
|
||||||
for cost in costs
|
|
||||||
])
|
|
||||||
related = accrual.AccrualPostings(data.Posting.from_txn(txn))
|
|
||||||
assert related.cost is related.INCONSISTENT
|
|
||||||
assert related.costs == costs
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('meta_key,acct_name', testutil.combine_values(
|
@pytest.mark.parametrize('meta_key,acct_name', testutil.combine_values(
|
||||||
CONSISTENT_METADATA,
|
CONSISTENT_METADATA,
|
||||||
ACCOUNTS,
|
ACCOUNTS,
|
||||||
|
|
Loading…
Reference in a new issue