diff --git a/conservancy_beancount/reports/accrual.py b/conservancy_beancount/reports/accrual.py index 3fb4e5bad134d52df1b54ad00909340a3a88a8d9..5dd181ed9264f1311620ae9e7c02454569478a8f 100644 --- a/conservancy_beancount/reports/accrual.py +++ b/conservancy_beancount/reports/accrual.py @@ -193,7 +193,7 @@ class AccrualPostings(core.RelatedPostings): norm_func = self.accrual_type.normalize_amount entity_pred = lambda post: norm_func(post.units).number > 0 self.entity = self._single_item(self.entities(entity_pred)) - self.invoice = self._single_item(self.first_links('invoice')) + self.invoice = self._single_item(self.first_meta_links('invoice', None)) self.end_balance = norm_func(self.balance_at_cost()) def _single_item(self, seq: Iterable[T]) -> Union[T, Sentinel]: @@ -213,9 +213,6 @@ class AccrualPostings(core.RelatedPostings): if pred(post) and 'entity' in post.meta ) - def first_links(self, key: MetaKey, default: Optional[str]=None) -> Iterator[Optional[str]]: - return (post.meta.first_link(key, default) for post in self) - def make_consistent(self) -> Iterator[Tuple[MetaValue, 'AccrualPostings']]: account_ok = isinstance(self.account, str) entity_ok = isinstance(self.entity, str) @@ -506,7 +503,7 @@ class OutgoingReport(BaseReport): self.rt_wrapper = rtutil.RT(rt_client) def _primary_rt_id(self, posts: AccrualPostings) -> rtutil.TicketAttachmentIds: - rt_ids = {url for url in posts.first_links('rt-id') if url is not None} + rt_ids = list(posts.first_meta_links('rt-id')) rt_ids_count = len(rt_ids) if rt_ids_count != 1: raise ValueError(f"{rt_ids_count} rt-id links found") diff --git a/conservancy_beancount/reports/core.py b/conservancy_beancount/reports/core.py index c2b9a533335836019fc8bedf9e02e7639fc42db3..3d3dbc2c7a177ccc489aa3307f95e8cca6637882 100644 --- a/conservancy_beancount/reports/core.py +++ b/conservancy_beancount/reports/core.py @@ -304,6 +304,23 @@ class RelatedPostings(Sequence[data.Posting]): def all_meta_links(self, key: MetaKey) -> Iterator[str]: return filters.iter_unique(self._all_meta_links(key)) + @overload + def first_meta_links(self, key: MetaKey, default: str='') -> Iterator[str]: ... + + @overload + def first_meta_links(self, key: MetaKey, default: None) -> Iterator[Optional[str]]: ... + + def first_meta_links(self, + key: MetaKey, + default: Optional[str]='', + ) -> Iterator[Optional[str]]: + retval = filters.iter_unique( + post.meta.first_link(key, default) for post in self + ) + if default == '': + retval = (s for s in retval if s) + return retval + def iter_with_balance(self) -> Iterator[Tuple[data.Posting, Balance]]: balance = MutableBalance() for post in self: diff --git a/tests/test_reports_related_postings.py b/tests/test_reports_related_postings.py index 6c0fe20762404f15e89d34a089a7573c1d37546c..8700f9412f7884d3465890454332dd49c5e6d3f2 100644 --- a/tests/test_reports_related_postings.py +++ b/tests/test_reports_related_postings.py @@ -275,6 +275,20 @@ def test_all_meta_links_preserves_order(): ) for c in '121323') assert list(related.all_meta_links('approval')) == list('123') +def test_first_meta_links(): + related = core.RelatedPostings(testutil.Posting( + 'Assets:Cash', 10, contract=value, _meta_type=data.Metadata, + ) for value in ['1 2', '', '1 3', testutil.PAST_DATE, '2 3', None]) + del related[-1].meta['contract'] + assert list(related.first_meta_links('contract')) == list('12') + +def test_first_meta_links_fallback(): + related = core.RelatedPostings(testutil.Posting( + 'Assets:Cash', 10, contract=value, _meta_type=data.Metadata, + ) for value in ['1 2', testutil.PAST_DATE, '1 3', None, '2 3']) + del related[-2].meta['contract'] + assert list(related.first_meta_links('contract', None)) == ['1', None, '2'] + def test_group_by_meta_zero(): assert not list(core.RelatedPostings.group_by_meta([], 'metacurrency'))