diff --git a/pyproject.toml b/pyproject.toml index 3d467ee2dd..ff0b9a961e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -165,7 +165,6 @@ documents = "rero_ils.modules.documents.api_views:api_blueprint" circ_policies = "rero_ils.modules.circ_policies.views:blueprint" local_entities = "rero_ils.modules.entities.local_entities.views:api_blueprint" remote_entities = "rero_ils.modules.entities.remote_entities.views:api_blueprint" -documents = "rero_ils.modules.documents.views:api_blueprint" holdings = "rero_ils.modules.holdings.api_views:api_blueprint" imports = "rero_ils.modules.imports.views:api_blueprint" item_types = "rero_ils.modules.item_types.views:blueprint" diff --git a/rero_ils/modules/documents/api.py b/rero_ils/modules/documents/api.py index c7ab886079..e5141cf87c 100644 --- a/rero_ils/modules/documents/api.py +++ b/rero_ils/modules/documents/api.py @@ -161,27 +161,125 @@ def _validate(self, **kwargs): return json @classmethod - def is_available(cls, pid, view_code, raise_exception=False): - """Get availability for document.""" - from ..holdings.api import Holding + def get_n_available_holdings(cls, pid, org_pid=None): + """Get the number of available and electronic holdings. + + :param pid: str - the document pid. + :param org_pid: str - the organisation pid. + :returns: int, int - the number of available and electronic holdings. + """ + from rero_ils.modules.holdings.api import HoldingsSearch + + # create the holding search query + holding_query = HoldingsSearch().available_query() + + # filter by the current document + filters = Q('term', document__pid=pid) + + # filter by organisation + if org_pid: + filters &= Q('term', organisation__pid=org_pid) + holding_query = holding_query.filter(filters) + + # get the number of electronic holdings + n_electronic_holdings = holding_query\ + .filter('term', holdings_type='electronic') + + return holding_query.count(), n_electronic_holdings.count() + + @classmethod + def get_available_item_pids(cls, pid, org_pid=None): + """Get the list of the available item pids. + + :param pid: str - the document pid. + :param org_pid: str - the organisation pid. + :returns: [str] - the list of the available item pids. + """ + from rero_ils.modules.items.api import ItemsSearch + + # create the item query + items_query = ItemsSearch().available_query() + + # filter by the current document + filters = Q('term', document__pid=pid) + + # filter by organisation + if org_pid: + filters &= Q('term', organisation__pid=org_pid) + + return [ + hit.pid for hit in items_query.filter(filters).source('pid').scan() + ] + + @classmethod + def get_item_pids_with_active_loan(cls, pid, org_pid=None): + """Get the list of items pids that have active loans. + + :param pid: str - the document pid. + :param org_pid: str - the organisation pid. + :returns: [str] - the list of the item pids having active loans. + """ + from rero_ils.modules.loans.api import LoansSearch + + loan_query = LoansSearch().unavailable_query() + + # filter by the current document + filters = Q('term', document_pid=pid) + + # filter by organisation + if org_pid: + filters &= Q('term', organisation__pid=org_pid) + + loan_query = loan_query.filter(filters) + + return [ + hit.item_pid.value for hit in loan_query.source('item_pid').scan() + ] + + @classmethod + def is_available(cls, pid, view_code=None): + """Get availability for document. + + Note: if the logic has to be changed here please check also for items + and holdings availability. + + :param pid: str - document pid value. + :param view_code: str - the view code. + """ + # get the organisation pid corresponding to the view code + org_pid = None if view_code != current_app.config.get( 'RERO_ILS_SEARCH_GLOBAL_VIEW_CODE'): - view_id = Organisation.get_record_by_viewcode(view_code)['pid'] - holding_pids = Holding.get_holdings_pid_by_document_pid_by_org( - pid, view_id) - else: - holding_pids = Holding.get_holdings_pid_by_document_pid(pid) - for holding_pid in holding_pids: - if holding := Holding.get_record_by_pid(holding_pid): - if holding.available: - return True - else: - msg = f'No holding: {holding_pid} in DB ' \ - f'for document: {pid}' - current_app.logger.error(msg) - if raise_exception: - raise ValueError(msg) - return False + org_pid = Organisation.get_record_by_viewcode(view_code)['pid'] + + # -------------- Holdings -------------------- + # get the number of available and electronic holdings + n_available_holdings, n_electronic_holdings = \ + cls.get_n_available_holdings(pid, org_pid) + + # available if an electronic holding exists + if n_electronic_holdings: + return True + + # unavailable if no holdings exists + if not n_available_holdings: + return False + + # -------------- Items -------------------- + # get the available item pids + available_item_pids = cls.get_available_item_pids(pid, org_pid) + + # unavailable if no items exists + if not available_item_pids: + return False + + # --------------- Loans ------------------- + # get item pids that have active loans + unavailable_item_pids = \ + cls.get_item_pids_with_active_loan(pid, org_pid) + + # available if at least one item don't have active loan + return bool(set(available_item_pids) - set(unavailable_item_pids)) @property def harvested(self): diff --git a/rero_ils/modules/documents/views.py b/rero_ils/modules/documents/views.py index 647f7dc3ed..4860d1a30e 100644 --- a/rero_ils/modules/documents/views.py +++ b/rero_ils/modules/documents/views.py @@ -24,9 +24,7 @@ import click from elasticsearch_dsl.query import Q -from flask import Blueprint, current_app, render_template -from flask import url_for -from flask import Blueprint, current_app, render_template +from flask import Blueprint, current_app, render_template, url_for from flask_babelex import gettext as _ from flask_login import current_user from invenio_records_ui.signals import record_viewed @@ -41,7 +39,7 @@ from rero_ils.modules.locations.api import Location from rero_ils.modules.organisations.api import Organisation from rero_ils.modules.patrons.api import current_patrons -from rero_ils.modules.utils import cached, extracted_data_from_ref +from rero_ils.modules.utils import extracted_data_from_ref from .api import Document, DocumentsSearch from .extensions import EditionStatementExtension, \ diff --git a/rero_ils/modules/holdings/api.py b/rero_ils/modules/holdings/api.py index 8071357d66..ba4ffea9e0 100644 --- a/rero_ils/modules/holdings/api.py +++ b/rero_ils/modules/holdings/api.py @@ -84,6 +84,14 @@ class Meta: default_filter = None + def available_query(self): + """Base query for holding availability. + + :returns: a filtered Elasticsearch query. + """ + # should not masked + return self.exclude('term', _masked=True) + class Holding(IlsRecord): """Holding class.""" @@ -260,14 +268,65 @@ def vendor(self): if self.get('vendor'): return extracted_data_from_ref(self.get('vendor'), data='record') - @property - def available(self): - """Get availability for holding.""" + def get_available_item_pids(self): + """Get the list of the available item pids. + + :returns: [str] - the list of the available item pids. + """ + from rero_ils.modules.items.api import ItemsSearch + items_query = ItemsSearch().available_query() + filters = Q('term', holding__pid=self.pid) + return [ + hit.pid for hit in items_query.filter(filters).source('pid').scan() + ] + + def get_item_pids_with_active_loan(self, item_pids): + """Get the list of items pids that have active loans. + + :param item_pids: [str] - the list of the item pids. + :returns: the list of the item pids having active loans. + """ + from rero_ils.modules.loans.api import LoansSearch + + loan_query = LoansSearch().unavailable_query() + + # the loans corresponding to the given item pids + loan_query = loan_query.filter(Q('terms', item_pid__value=item_pids)) + + return [ + hit.item_pid.value for hit in loan_query.source('item_pid').scan() + ] + + def is_available(self): + """Get availability for the current holding. + + Note: if the logic has to be changed here please check also for items + and documents availability. + """ + # -------------- Holdings -------------------- + # unavailable if masked + if self.get('_masked', False): + return False + + # available if the holding is electronic if self.is_electronic: return True - if self.get('_masked', False): + + # -------------- Items -------------------- + # get available item pids + available_item_pids = self.get_available_item_pids() + + # unavailable if no item exists + if not available_item_pids: return False - return self._exists_available_child() + + # --------------- Loans ------------------- + # get item pids that have active loans + unavailable_item_pids = \ + self.get_item_pids_with_active_loan(available_item_pids) + + # available if at least one item don't have active loan + return bool(set(available_item_pids) - set(unavailable_item_pids)) @property def max_number_of_claims(self): @@ -303,14 +362,6 @@ def get_note(self, note_type): if note.get('type') == note_type] return next(iter(notes), None) - def _exists_available_child(self): - """Check if at least one child of this holding is available.""" - for pid in Item.get_items_pid_by_holding_pid(self.pid): - item = Item.get_record_by_pid(pid) - if item.available: - return True - return False - @property def get_items_count_by_holding_pid(self): """Returns items count from holding pid.""" diff --git a/rero_ils/modules/holdings/api_views.py b/rero_ils/modules/holdings/api_views.py index 7a638f498c..92194bf9fa 100644 --- a/rero_ils/modules/holdings/api_views.py +++ b/rero_ils/modules/holdings/api_views.py @@ -315,6 +315,6 @@ def holding_availability(pid): """HTTP GET request for holding availability.""" if holding := Holding.get_record_by_pid(pid): return jsonify({ - 'available': holding.available + 'available': holding.is_available() }) abort(404) diff --git a/rero_ils/modules/items/api/api.py b/rero_ils/modules/items/api/api.py index 3bf1022487..337a10ebe7 100644 --- a/rero_ils/modules/items/api/api.py +++ b/rero_ils/modules/items/api/api.py @@ -21,12 +21,14 @@ from functools import partial from elasticsearch.exceptions import NotFoundError +from elasticsearch_dsl import Q from invenio_search import current_search_client from rero_ils.modules.api import IlsRecordError, IlsRecordsIndexer, \ IlsRecordsSearch from rero_ils.modules.documents.api import DocumentsSearch from rero_ils.modules.fetchers import id_fetcher +from rero_ils.modules.item_types.api import ItemTypesSearch from rero_ils.modules.minters import id_minter from rero_ils.modules.organisations.api import Organisation from rero_ils.modules.patrons.api import current_librarian @@ -62,6 +64,37 @@ class Meta: default_filter = None + def available_query(self): + """Base elasticsearch query to compute availability. + + :returns: elasticsearch query. + """ + must_not_filters = [ + # should not be masked + Q('term', _masked=True), + # if issue the status should be received + Q('exists', field='issue') & ~Q('term', issue__status='received') + ] + + # negative availability item types + not_available_item_types = [ + hit.pid for hit in ItemTypesSearch() + .source('pid') + .filter('term', negative_availability=True) + .scan() + ] + if not_available_item_types: + # negative availability item type and not temporary item types + has_items_filters = \ + Q('terms', item_type__pid=not_available_item_types) + has_items_filters &= ~Q('exists', field='temporary_item_type') + # temporary item types with negative availability + has_items_filters |= Q( + 'terms', temporary_item_type__pid=not_available_item_types) + # add to the must not filters + must_not_filters.append(has_items_filters) + return self.filter(Q('bool', must_not=must_not_filters)) + class Item(ItemCirculation, ItemIssue): """Item class.""" diff --git a/rero_ils/modules/items/api/circulation.py b/rero_ils/modules/items/api/circulation.py index 4ae7deeb58..9c6e8659cc 100644 --- a/rero_ils/modules/items/api/circulation.py +++ b/rero_ils/modules/items/api/circulation.py @@ -1276,26 +1276,23 @@ def get_loan_states_for_an_item(self): ]).params(preserve_order=True).source(['state']) return list(dict.fromkeys([result.state for result in search.scan()])) - @property - def available(self): + def is_available(self): """Get availability for item. - An item is 'available' if there are no related request/active_loan and - if the related circulation category doesn't specify a negative - availability. - All masked items are considered as unavailable. + Note: if the logic has to be changed here please check also for + documents and holdings availability. """ - if self.get('_masked', False): - return False - if self.circulation_category.get('negative_availability'): - return False - if self.temp_item_type_negative_availability: - return False - if self.is_issue and self.issue_status != ItemIssueStatus.RECEIVED: - return False - if self.item_has_active_loan_or_request() > 0: + from ..api import ItemsSearch + + items_query = ItemsSearch().available_query() + + # check item availability + if not items_query.filter('term', pid=self.pid).count(): return False - return True + + # --------------- Loans ------------------- + # unavailable if the current item has active loans + return not self.item_has_active_loan_or_request() @property def availability_text(self): diff --git a/rero_ils/modules/items/utils.py b/rero_ils/modules/items/utils.py index 0b3057588b..2d6474d8b6 100644 --- a/rero_ils/modules/items/utils.py +++ b/rero_ils/modules/items/utils.py @@ -89,7 +89,7 @@ def exists_available_item(items=None): item = Item.get_record_by_pid(item) if not isinstance(item, Item): raise ValueError('All items should be Item resource.') - if item.available: + if item.is_available(): return True return False diff --git a/rero_ils/modules/items/views/api_views.py b/rero_ils/modules/items/views/api_views.py index 5fb3717a4a..b8409172b2 100644 --- a/rero_ils/modules/items/views/api_views.py +++ b/rero_ils/modules/items/views/api_views.py @@ -472,7 +472,7 @@ def item_availability(pid): item = Item.get_record_by_pid(pid) if not item: abort(404) - data = dict(available=item.available) + data = dict(available=item.is_available()) if flask_request.args.get('more_info'): extra = { 'status': item['status'], diff --git a/rero_ils/modules/loans/api.py b/rero_ils/modules/loans/api.py index 27bf8b909b..ea9b543c4a 100644 --- a/rero_ils/modules/loans/api.py +++ b/rero_ils/modules/loans/api.py @@ -72,6 +72,17 @@ class Meta: default_filter = None + def unavailable_query(self): + """Base query to compute item availability. + + Unavailable if some active loans exists. + + :returns: an elasticsearch query + """ + states = [LoanState.PENDING] + \ + current_app.config['CIRCULATION_STATES_LOAN_ACTIVE'] + return self.filter('terms', state=states) + class Loan(IlsRecord): """Loan class.""" diff --git a/tests/api/items/test_items_rest_views.py b/tests/api/items/test_items_rest_views.py index 54d3f2cb23..29b678e29d 100644 --- a/tests/api/items/test_items_rest_views.py +++ b/tests/api/items/test_items_rest_views.py @@ -42,7 +42,7 @@ def test_item_dumps(client, item_lib_martigny, org_martigny, assert res.status_code == 200 item_es = Item(get_json(res).get('metadata')) - assert item_es.available + assert item_es.is_available() assert item_es.organisation_pid == org_martigny.pid diff --git a/tests/api/loans/test_loans_rest.py b/tests/api/loans/test_loans_rest.py index 6a30e6f112..bfe80232dd 100644 --- a/tests/api/loans/test_loans_rest.py +++ b/tests/api/loans/test_loans_rest.py @@ -209,7 +209,7 @@ def test_due_soon_loans(client, librarian_martigny, can, reasons = item.can_delete assert can assert reasons == {} - assert item.available + assert item.is_available() assert not get_last_transaction_loc_for_item(item_pid) assert not item.patron_has_an_active_loan_on_item(patron_martigny) @@ -420,7 +420,7 @@ def test_checkout_item_transit(client, mailbox, item2_lib_martigny, loc_public_martigny, circulation_policies): """Test checkout of an item in transit.""" - assert item2_lib_martigny.available + assert item2_lib_martigny.is_available() mailbox.clear() # request @@ -447,7 +447,7 @@ def test_checkout_item_transit(client, mailbox, item2_lib_martigny, assert res.status_code == 200 actions = data.get('action_applied') loan_pid = actions[LoanAction.REQUEST].get('pid') - assert not item2_lib_martigny.available + assert not item2_lib_martigny.is_available() assert len(mailbox) == 1 assert mailbox[-1].recipients == [ @@ -477,9 +477,9 @@ def test_checkout_item_transit(client, mailbox, item2_lib_martigny, ) ) assert res.status_code == 200 - assert not item2_lib_martigny.available + assert not item2_lib_martigny.is_available() item = Item.get_record_by_pid(item2_lib_martigny.pid) - assert not item.available + assert not item.is_available() loan = Loan.get_record_by_pid(loan_pid) assert loan['state'] == LoanState.ITEM_IN_TRANSIT_FOR_PICKUP @@ -497,9 +497,9 @@ def test_checkout_item_transit(client, mailbox, item2_lib_martigny, ) ) assert res.status_code == 200 - assert not item2_lib_martigny.available + assert not item2_lib_martigny.is_available() item = Item.get_record_by_pid(item2_lib_martigny.pid) - assert not item.available + assert not item.is_available() loan_before_checkout = get_loan_for_item(item_pid_to_object(item.pid)) assert loan_before_checkout.get('state') == LoanState.ITEM_AT_DESK @@ -640,7 +640,7 @@ def test_librarian_request_on_blocked_user( patron3_martigny_blocked, circulation_policies): """Librarian request on blocked user returns a specific 403 message.""" - assert item_lib_martigny.available + assert item_lib_martigny.is_available() # request login_user_via_session(client, librarian_martigny.user) diff --git a/tests/api/test_availability.py b/tests/api/test_availability.py index e859124a5d..c7a446e9d6 100644 --- a/tests/api/test_availability.py +++ b/tests/api/test_availability.py @@ -202,8 +202,8 @@ def test_item_holding_document_availability( """Test item, holding and document availability.""" assert item_availablity_status( client, item_lib_martigny.pid, librarian_martigny.user) - assert item_lib_martigny.available - assert holding_lib_martigny.available + assert item_lib_martigny.is_available() + assert holding_lib_martigny.is_available() assert holding_lib_martigny.get_holding_loan_conditions() == 'standard' assert Document.is_available(document.pid, view_code='global') assert document_availability_status( @@ -235,11 +235,11 @@ def test_item_holding_document_availability( assert res.status_code == 200 actions = data.get('action_applied') loan_pid = actions[LoanAction.REQUEST].get('pid') - assert not item_lib_martigny.available + assert not item_lib_martigny.is_available() assert not item_availablity_status( client, item_lib_martigny.pid, librarian_martigny.user) holding = Holding.get_record_by_pid(holding_lib_martigny.pid) - assert holding.available + assert holding.is_available() assert holding_lib_martigny.get_holding_loan_conditions() == 'standard' assert Document.is_available(document.pid, 'global') assert document_availability_status( @@ -257,12 +257,12 @@ def test_item_holding_document_availability( ) ) assert res.status_code == 200 - assert not item_lib_martigny.available + assert not item_lib_martigny.is_available() assert not item_availablity_status( client, item_lib_martigny.pid, librarian_martigny.user) - assert not item_lib_martigny.available + assert not item_lib_martigny.is_available() holding = Holding.get_record_by_pid(holding_lib_martigny.pid) - assert holding.available + assert holding.is_available() assert holding_lib_martigny.get_holding_loan_conditions() == 'standard' assert Document.is_available(document.pid, 'global') assert document_availability_status( @@ -280,13 +280,13 @@ def test_item_holding_document_availability( ) ) assert res.status_code == 200 - assert not item_lib_martigny.available + assert not item_lib_martigny.is_available() assert not item_availablity_status( client, item_lib_martigny.pid, librarian_saxon.user) item = Item.get_record_by_pid(item_lib_martigny.pid) - assert not item.available + assert not item.is_available() holding = Holding.get_record_by_pid(holding_lib_martigny.pid) - assert holding.available + assert holding.is_available() assert holding_lib_martigny.get_holding_loan_conditions() == 'standard' assert Document.is_available(document.pid, 'global') assert document_availability_status( @@ -305,20 +305,20 @@ def test_item_holding_document_availability( assert res.status_code == 200 item = Item.get_record_by_pid(item_lib_martigny.pid) - assert not item.available + assert not item.is_available() assert not item_availablity_status( client, item.pid, librarian_martigny.user) holding = Holding.get_record_by_pid(holding_lib_martigny.pid) - assert holding.available + assert holding.is_available() assert holding_lib_martigny.get_holding_loan_conditions() == 'standard' assert Document.is_available(document.pid, 'global') assert document_availability_status( client, document.pid, librarian_martigny.user) - # masked item isn't available + # masked item isn't.is_available() item['_masked'] = True item = item.update(item, dbcommit=True, reindex=True) - assert not item.available + assert not item.is_available() del item['_masked'] item.update(item, dbcommit=True, reindex=True) @@ -367,11 +367,11 @@ def test_item_holding_document_availability( ) ) assert res.status_code == 200 - assert not item2_lib_martigny.available + assert not item2_lib_martigny.is_available() assert not item_availablity_status( client, item2_lib_martigny.pid, librarian_martigny.user) holding = Holding.get_record_by_pid(holding_lib_martigny.pid) - assert not holding.available + assert not holding.is_available() assert holding_lib_martigny.get_holding_loan_conditions() == 'standard' assert not Document.is_available(document.pid, 'global') assert not document_availability_status( @@ -380,7 +380,6 @@ def test_item_holding_document_availability( def item_availablity_status(client, pid, user): """Returns item availability.""" - login_user_via_session(client, user) res = client.get( url_for( 'api_item.item_availability', @@ -394,7 +393,6 @@ def item_availablity_status(client, pid, user): def document_availability_status(client, pid, user): """Returns document availability.""" - login_user_via_session(client, user) res = client.get( url_for( 'api_documents.document_availability', @@ -436,9 +434,9 @@ def test_availability_cipo_allow_request( cipo.update(cipo.dumps(), dbcommit=True, reindex=True) -def test_document_availability_failed(client, librarian2_martigny): +def test_document_availability_failed( + client, item_lib_martigny, document_with_issn, org_martigny): """Test document availability with dummy data should failed.""" - login_user_via_session(client, librarian2_martigny.user) res = client.get( url_for( 'api_documents.document_availability', @@ -446,11 +444,27 @@ def test_document_availability_failed(client, librarian2_martigny): ) ) assert res.status_code == 404 + res = client.get( + url_for( + 'api_documents.document_availability', + pid=document_with_issn.pid + ) + ) + assert res.status_code == 200 + assert not res.json.get('available') + res = client.get( + url_for( + 'api_documents.document_availability', + pid=document_with_issn.pid, + view_code=org_martigny['code'] + ) + ) + assert res.status_code == 200 + assert not res.json.get('available') def test_item_availability_failed(client, librarian2_martigny): """Test item availability with dummy data should failed.""" - login_user_via_session(client, librarian2_martigny.user) res = client.get( url_for( 'api_item.item_availability', diff --git a/tests/ui/documents/test_documents_api.py b/tests/ui/documents/test_documents_api.py index 0ee5a57f2a..2ac96c389f 100644 --- a/tests/ui/documents/test_documents_api.py +++ b/tests/ui/documents/test_documents_api.py @@ -40,21 +40,6 @@ from rero_ils.modules.tasks import process_bulk_queue -def test_document_properties(document): - """Test document properties.""" - # As no item/holding are loaded into this test, `is_available` should - # return false/raise an error. - assert not Document.is_available(document.pid, 'global') - - with mock.patch( - 'rero_ils.modules.holdings.api.Holding.' - 'get_holdings_pid_by_document_pid', - mock.MagicMock(return_value=['not_exists']) - ): - with pytest.raises(ValueError): - Document.is_available(document.pid, 'global', raise_exception=True) - - def test_document_create(db, document_data_tmp): """Test document creation.""" ptty = Document.create(document_data_tmp, delete_pid=True) diff --git a/tests/ui/holdings/test_holdings_api.py b/tests/ui/holdings/test_holdings_api.py index efef507ca5..0e136928c4 100644 --- a/tests/ui/holdings/test_holdings_api.py +++ b/tests/ui/holdings/test_holdings_api.py @@ -72,9 +72,10 @@ def test_holding_availability(holding_lib_sion_electronic, holding_lib_martigny, item_lib_martigny): """Test holding availability.""" # An electronic holding is always available despite if no item are linked - assert holding_lib_sion_electronic.available + assert holding_lib_sion_electronic.is_available() # The availability of other holdings type depends on children availability - assert holding_lib_martigny.available == item_lib_martigny.available + assert holding_lib_martigny.is_available() == \ + item_lib_martigny.is_available() def test_holding_extended_validation( @@ -162,4 +163,4 @@ def test_holdings_properties(holding_lib_martigny_w_patterns, vendor_martigny): assert holding.days_before_next_claim == 7 holding['_masked'] = True - assert not holding.available + assert not holding.is_available() diff --git a/tests/ui/items/test_items_api.py b/tests/ui/items/test_items_api.py index 796b659834..249a13634e 100644 --- a/tests/ui/items/test_items_api.py +++ b/tests/ui/items/test_items_api.py @@ -246,13 +246,13 @@ def test_items_availability(item_type_missing_martigny, item = Item.create(item_data, dbcommit=True, reindex=True) # test the availability and availability_text - assert not item.available + assert not item.is_available() assert len(item.availability_text) == \ len(item_type_missing_martigny.get('displayed_status', [])) + 1 del item['temporary_item_type'] item = item.update(item, dbcommit=True, reindex=True) - assert item.available + assert item.is_available() assert len(item.availability_text) == 1 # only default value # test availability and availability_text for an issue @@ -265,12 +265,12 @@ def test_items_availability(item_type_missing_martigny, 'expected_date': '1970-01-01' } item = item.update(item, dbcommit=True, reindex=True) - assert item.available + assert item.is_available() assert item.availability_text[0]['label'] == item.status item['issue']['status'] = ItemIssueStatus.LATE item = item.update(item, dbcommit=True, reindex=True) - assert not item.available + assert not item.is_available() assert item.availability_text[0]['label'] == ItemIssueStatus.LATE # delete the created item