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…
	
	Add table
		
		Reference in a new issue