Skip to content

Commit

Permalink
circulation: manage non-circulating libraries
Browse files Browse the repository at this point in the history
In older versions, the transaction locations of a library are
selected from the list of pickup locations of the library.
This causes a problem for libraries have not setup a pickup location.

With this commit, for libraries with no pickup locations
the system considers the first library location as transaction
location for circulation transactions.

* Allows requests on items of non-circulating libraries at external locations.
* Closes rero#2367
* Adds a non-circulating library to units testing.

Co-Authored-by: Aly Badr <aly.badr@rero.ch>
  • Loading branch information
Aly Badr committed Oct 5, 2021
1 parent dd87109 commit 735cb75
Show file tree
Hide file tree
Showing 7 changed files with 307 additions and 4 deletions.
2 changes: 1 addition & 1 deletion rero_ils/modules/items/api/circulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def complete_action_missing_params(
if transaction_library_pid is not None:
lib = Library.get_record_by_pid(transaction_library_pid)
kwargs['transaction_location_pid'] = \
lib.get_pickup_location_pid()
lib.get_transaction_location_pid()

return loan, kwargs

Expand Down
12 changes: 12 additions & 0 deletions rero_ils/modules/libraries/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ def pickup_location_query(self):
'term', library__pid=self.pid).filter(
'term', is_pickup=True).source(['pid']).scan()

def transaction_locations_query(self):
"""Search the location index for a transaction location."""
return LocationsSearch().filter(
'term', library__pid=self.pid).source(['pid']).scan()

def get_pickup_locations_pids(self):
"""Returns libraries all pickup locations pids."""
for location in self.pickup_location_query():
Expand All @@ -117,6 +122,13 @@ def get_pickup_location_pid(self):
except StopIteration:
return None

def get_transaction_location_pid(self):
"""Returns libraries first transaction location pid."""
try:
return next(self.pickup_location_query()).pid
except StopIteration:
return next(self.transaction_locations_query()).pid

def _is_betweentimes(self, time_to_test, times):
"""Test if time is between times."""
times_open = False
Expand Down
43 changes: 43 additions & 0 deletions tests/api/circulation/test_library_with_no_circulation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
#
# RERO ILS
# Copyright (C) 2020 RERO
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Tests REST checkout API methods in library with no circulation."""

from invenio_accounts.testutils import login_user_via_session
from utils import postdata


def test_requesting_item_from_non_circulating_library(
client, librarian_martigny, lib_martigny, lib_martigny_bourg,
patron_martigny, loc_public_martigny, loc_public_martigny_bourg,
item_lib_martigny_bourg, circulation_policies, patron2_martigny):
"""Test requests at non circulating library."""
# TEST: a librarian from an external library can request and item from a
# non circulation library to be picked-up at his own library.
login_user_via_session(client, librarian_martigny.user)
res, data = postdata(
client,
'api_item.librarian_request',
dict(
item_pid=item_lib_martigny_bourg.pid,
patron_pid=patron_martigny.pid,
pickup_location_pid=loc_public_martigny.pid,
transaction_library_pid=lib_martigny_bourg.pid,
transaction_user_pid=librarian_martigny.pid
)
)
assert res.status_code == 200
11 changes: 8 additions & 3 deletions tests/api/libraries/test_libraries_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,14 @@ def test_libraries_post_put_delete(client, lib_martigny_data, json_header):
assert res.status_code == 410


def test_library_no_pickup(lib_sion):
"""Test library with no pick_up location."""
def test_non_circulating_libraries(
lib_sion, lib_martigny, lib_martigny_bourg, loc_public_martigny,
loc_public_martigny_bourg):
"""Test pickup vs transaction locations."""
assert not lib_sion.get_pickup_location_pid()
assert not lib_martigny_bourg.get_pickup_location_pid()
assert lib_martigny.get_pickup_location_pid()
assert lib_martigny_bourg.get_transaction_location_pid()


def test_library_never_open(lib_sion):
Expand Down Expand Up @@ -186,7 +191,7 @@ def test_filtered_libraries_get(
res = client.get(list_url)
assert res.status_code == 200
data = get_json(res)
assert data['hits']['total']['value'] == 3
assert data['hits']['total']['value'] == 4

# Sion
login_user_via_session(client, librarian_sion.user)
Expand Down
183 changes: 183 additions & 0 deletions tests/data/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,152 @@
],
"communication_language": "fre"
},
"lib7": {
"$schema": "https://bib.rero.ch/schemas/libraries/library-v0.0.1.json",
"address": "Ave de la gare, Martigny 1920",
"code": "MARTIGNYBOURG",
"email": "reroilstest+martignybourg@gmail.com",
"name": "Library of Martigny-bourg",
"organisation": {
"$ref": "https://bib.rero.ch/api/organisations/org1"
},
"pid": "lib7",
"opening_hours": [
{
"day": "monday",
"is_open": true,
"times": [
{
"start_time": "07:00",
"end_time": "19:00"
}
]
},
{
"day": "tuesday",
"is_open": true,
"times": [
{
"start_time": "07:00",
"end_time": "19:00"
}
]
},
{
"day": "wednesday",
"is_open": true,
"times": [
{
"start_time": "07:00",
"end_time": "19:00"
}
]
},
{
"day": "thursday",
"is_open": true,
"times": [
{
"start_time": "07:00",
"end_time": "19:00"
}
]
},
{
"day": "friday",
"is_open": true,
"times": [
{
"start_time": "07:00",
"end_time": "19:00"
}
]
},
{
"day": "saturday",
"is_open": false,
"times": []
},
{
"day": "sunday",
"is_open": false,
"times": []
}
],
"exception_dates": [
{
"end_date": "2019-01-06",
"is_open": false,
"start_date": "2018-12-22",
"title": "Vacances de No\u00ebl",
"repeat": {
"interval": 1,
"period": "yearly"
}
},
{
"is_open": true,
"start_date": "2018-12-15",
"times": [
{
"end_time": "16:00",
"start_time": "10:00"
}
],
"title": "Samedi du livre"
},
{
"is_open": false,
"repeat": {
"interval": 1,
"period": "yearly"
},
"start_date": "2019-08-01",
"title": "1er ao\u00fbt"
},
{
"is_open": false,
"repeat": {
"interval": 2,
"period": "monthly"
},
"start_date": "2019-01-01",
"title": "1er du mois, 1 mois sur 2"
}
],
"notification_settings": [
{
"type": "due_soon",
"email": "reroilstest+martignybourg@gmail.com"
},
{
"type": "overdue",
"email": "reroilstest+martignybourg@gmail.com"
},
{
"type": "recall",
"email": "reroilstest+martignybourg@gmail.com"
},
{
"type": "availability",
"email": "reroilstest+martignybourg@gmail.com",
"delay": 0
},
{
"type": "request",
"email": "reroilstest+martignybourg@gmail.com"
},
{
"type": "transit_notice",
"email": "reroilstest+martignybourg@gmail.com"
},
{
"type": "booking",
"email": "reroilstest+martignybourg@gmail.com"
}
],
"communication_language": "fre"
},
"loc1": {
"$schema": "https://bib.rero.ch/schemas/locations/location-v0.0.1.json",
"code": "MARTIGNY-PUBLIC",
Expand Down Expand Up @@ -965,6 +1111,17 @@
"pickup_name": "SAILLON-PUBLIC: Public Space",
"allow_request": true
},
"loc15": {
"$schema": "https://bib.rero.ch/schemas/locations/location-v0.0.1.json",
"code": "MARTIGNY-BOURG-PUBLIC",
"name": "Martigny Bourg Library Public Space",
"pid": "loc15",
"library": {
"$ref": "https://bib.rero.ch/api/libraries/lib7"
},
"is_pickup": false,
"allow_request": true
},
"itty1": {
"$schema": "https://bib.rero.ch/schemas/item_types/item_type-v0.0.1.json",
"name": "standard",
Expand Down Expand Up @@ -3262,6 +3419,32 @@
},
"status": "on_shelf"
},
"item10": {
"$schema": "https://bib.rero.ch/schemas/items/item-v0.0.1.json",
"pid": "item10",
"barcode": "123410",
"type": "standard",
"document": {
"$ref": "https://bib.rero.ch/api/documents/doc1"
},
"call_number": "000010",
"location": {
"$ref": "https://bib.rero.ch/api/locations/loc15"
},
"library": {
"$ref": "https://bib.rero.ch/api/libraries/lib7"
},
"organisation": {
"$ref": "https://bib.rero.ch/api/organisations/org1"
},
"item_type": {
"$ref": "https://bib.rero.ch/api/item_types/itty1"
},
"status": "on_shelf",
"url": "https://lipda.mediatheque.ch/CH-000019-X:223156.file",
"pac_code": "0_frozen_collection",
"price": 15.2
},
"ptrn1": {
"$schema": "https://bib.rero.ch/schemas/patrons/patron-v0.0.1.json",
"pid": "ptrn1",
Expand Down
23 changes: 23 additions & 0 deletions tests/fixtures/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,12 @@ def item_lib_martigny_data(data):
return deepcopy(data.get('item1'))


@pytest.fixture(scope="module")
def item_lib_martigny_bourg_data(data):
"""Load item of martigny bourg library."""
return deepcopy(data.get('item10'))


@pytest.fixture(scope="function")
def item_lib_martigny_data_tmp(data):
"""Load item of martigny library scope function."""
Expand All @@ -433,6 +439,23 @@ def item_lib_martigny(
return item


@pytest.fixture(scope="module")
def item_lib_martigny_bourg(
app,
document,
item_lib_martigny_bourg_data,
loc_public_martigny_bourg,
item_type_standard_martigny):
"""Create item of martigny library bourg."""
item = Item.create(
data=item_lib_martigny_bourg_data,
delete_pid=False,
dbcommit=True,
reindex=True)
flush_index(ItemsSearch.Meta.index)
return item


@pytest.fixture(scope="module")
def item2_lib_martigny_data(data):
"""Load item of martigny library."""
Expand Down
Loading

0 comments on commit 735cb75

Please sign in to comment.