invenio-app-ils 4.6.0__py2.py3-none-any.whl → 5.0.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- invenio_app_ils/__init__.py +1 -1
- invenio_app_ils/assets/semantic-ui/less/theme.config +103 -0
- invenio_app_ils/circulation/api.py +9 -5
- invenio_app_ils/circulation/config.py +1 -0
- invenio_app_ils/circulation/loaders/schemas/json/loan_checkout.py +1 -1
- invenio_app_ils/circulation/loaders/schemas/json/loan_request.py +2 -2
- invenio_app_ils/circulation/notifications/api.py +9 -1
- invenio_app_ils/circulation/notifications/messages.py +2 -1
- invenio_app_ils/circulation/templates/invenio_app_ils_circulation/notifications/update_dates.html +19 -0
- invenio_app_ils/cli.py +92 -64
- invenio_app_ils/config.py +10 -2
- invenio_app_ils/documents/loaders/jsonschemas/document.py +1 -1
- invenio_app_ils/eitems/loaders/jsonschemas/eitems.py +2 -2
- invenio_app_ils/patrons/anonymization.py +0 -4
- invenio_app_ils/series/loaders/jsonschemas/series.py +1 -1
- invenio_app_ils/webpack.py +27 -0
- {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/METADATA +52 -42
- {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/RECORD +24 -112
- {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/entry_points.txt +3 -0
- {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/top_level.txt +0 -1
- invenio_app_ils/notifications/admin.py +0 -57
- tests/__init__.py +0 -8
- tests/api/__init__.py +0 -8
- tests/api/acquisition/__init__.py +0 -8
- tests/api/acquisition/test_acq_orders_crud.py +0 -99
- tests/api/acquisition/test_acq_orders_permissions.py +0 -104
- tests/api/acquisition/test_acq_providers_permissions.py +0 -83
- tests/api/circulation/__init__.py +0 -8
- tests/api/circulation/test_expired_loans.py +0 -88
- tests/api/circulation/test_loan_bulk_extend.py +0 -169
- tests/api/circulation/test_loan_checkout.py +0 -422
- tests/api/circulation/test_loan_default_durations.py +0 -43
- tests/api/circulation/test_loan_document_resolver.py +0 -18
- tests/api/circulation/test_loan_extend.py +0 -122
- tests/api/circulation/test_loan_item_permissions.py +0 -135
- tests/api/circulation/test_loan_item_resolver.py +0 -26
- tests/api/circulation/test_loan_list_permissions.py +0 -98
- tests/api/circulation/test_loan_patron_resolver.py +0 -38
- tests/api/circulation/test_loan_request.py +0 -338
- tests/api/circulation/test_loan_update.py +0 -150
- tests/api/circulation/test_notifications.py +0 -221
- tests/api/conftest.py +0 -227
- tests/api/document_requests/__init__.py +0 -8
- tests/api/document_requests/test_document_requests.py +0 -131
- tests/api/document_requests/test_document_requests_permissions.py +0 -159
- tests/api/document_requests/test_notifications_filter.py +0 -35
- tests/api/ill/__init__.py +0 -8
- tests/api/ill/test_ill_brw_crud.py +0 -74
- tests/api/ill/test_ill_brw_reqs_patron_loan_create_action.py +0 -198
- tests/api/ill/test_ill_brw_reqs_patron_loan_extension_actions.py +0 -280
- tests/api/ill/test_ill_brw_reqs_permissions.py +0 -163
- tests/api/ill/test_ill_providers_permissions.py +0 -82
- tests/api/ils/__init__.py +0 -8
- tests/api/ils/documents/__init__.py +0 -8
- tests/api/ils/documents/test_document_crud.py +0 -57
- tests/api/ils/documents/test_document_permissions.py +0 -100
- tests/api/ils/documents/test_document_resolvers.py +0 -35
- tests/api/ils/eitems/__init__.py +0 -8
- tests/api/ils/eitems/test_eitems_crud.py +0 -42
- tests/api/ils/eitems/test_eitems_permissions.py +0 -85
- tests/api/ils/eitems/test_files.py +0 -162
- tests/api/ils/items/__init__.py +0 -8
- tests/api/ils/items/test_apis.py +0 -43
- tests/api/ils/items/test_items_crud.py +0 -99
- tests/api/ils/items/test_items_permissions.py +0 -107
- tests/api/ils/records_relations/__init__.py +0 -8
- tests/api/ils/records_relations/helpers.py +0 -115
- tests/api/ils/records_relations/test_records_relations_parentchild.py +0 -560
- tests/api/ils/records_relations/test_records_relations_sequence.py +0 -294
- tests/api/ils/records_relations/test_records_relations_siblings.py +0 -751
- tests/api/ils/series/__init__.py +0 -8
- tests/api/ils/series/test_series_permissions.py +0 -95
- tests/api/ils/test_anonymization.py +0 -181
- tests/api/ils/test_apis.py +0 -76
- tests/api/ils/test_closures.py +0 -353
- tests/api/ils/test_errors.py +0 -125
- tests/api/ils/test_facets.py +0 -88
- tests/api/ils/test_internal_locations.py +0 -96
- tests/api/ils/test_loaders.py +0 -51
- tests/api/ils/test_metadata_extensions.py +0 -206
- tests/api/ils/test_notifications.py +0 -173
- tests/api/ils/test_notifications_mails.py +0 -37
- tests/api/ils/test_notifications_permissions.py +0 -55
- tests/api/ils/test_patrons.py +0 -102
- tests/api/ils/test_record_delete.py +0 -42
- tests/api/ils/test_record_permissions.py +0 -132
- tests/api/ils/test_resolvers.py +0 -205
- tests/api/ils/test_stats.py +0 -142
- tests/api/ils/test_tasks.py +0 -209
- tests/api/ils/test_vocabularies.py +0 -35
- tests/conftest.py +0 -221
- tests/data/acq_orders.json +0 -110
- tests/data/acq_providers.json +0 -12
- tests/data/document_requests.json +0 -77
- tests/data/documents.json +0 -238
- tests/data/eitems.json +0 -71
- tests/data/ill_borrowing_requests.json +0 -77
- tests/data/ill_providers.json +0 -12
- tests/data/internal_locations.json +0 -22
- tests/data/items.json +0 -304
- tests/data/loans.json +0 -133
- tests/data/loans_most_loaned.json +0 -128
- tests/data/locations.json +0 -26
- tests/data/series.json +0 -33
- tests/helpers.py +0 -107
- tests/templates/notifications/title_body.html +0 -8
- tests/templates/notifications/title_body_html.html +0 -13
- tests/templates/notifications/title_body_html_ctx.html +0 -13
- tests/templates/notifications/title_only.html +0 -3
- tests/test_post_logout_redirect.py +0 -23
- tests/test_version.py +0 -17
- /tests/templates/notifications/blank.html → /invenio_app_ils/assets/semantic-ui/templates/.gitkeep +0 -0
- {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/WHEEL +0 -0
- {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/licenses/AUTHORS.rst +0 -0
- {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Copyright (C) 2018-2020 CERN.
|
|
4
|
-
#
|
|
5
|
-
# invenio-app-ils is free software; you can redistribute it and/or modify it
|
|
6
|
-
# under the terms of the MIT License; see LICENSE file for more details.
|
|
7
|
-
|
|
8
|
-
"""Test loans item permissions."""
|
|
9
|
-
|
|
10
|
-
import json
|
|
11
|
-
from copy import deepcopy
|
|
12
|
-
|
|
13
|
-
from flask import url_for
|
|
14
|
-
|
|
15
|
-
from tests.helpers import user_login
|
|
16
|
-
|
|
17
|
-
NEW_LOAN = {
|
|
18
|
-
"item_pid": {"type": "pitmid", "value": "itemid-1"},
|
|
19
|
-
"document_pid": "docid-1",
|
|
20
|
-
"patron_pid": "1",
|
|
21
|
-
"transaction_location_pid": "locid-1",
|
|
22
|
-
"pickup_location_pid": "locid-1",
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def _fetch_loan(client, json_headers, loan):
|
|
27
|
-
"""Return the loan fetched with a REST call."""
|
|
28
|
-
url = url_for("invenio_records_rest.loanid_item", pid_value=loan["pid"])
|
|
29
|
-
return client.get(url, headers=json_headers)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def _assert_get_loan_success(client, json_headers, loan):
|
|
33
|
-
"""Assert that GET API call succeeded."""
|
|
34
|
-
res = _fetch_loan(client, json_headers, loan)
|
|
35
|
-
assert res.status_code == 200
|
|
36
|
-
data = json.loads(res.data.decode("utf-8"))["metadata"]
|
|
37
|
-
assert data["pid"] == loan["pid"]
|
|
38
|
-
assert data["patron_pid"] == loan["patron_pid"]
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def test_anonymous_cannot_get_any_loan(client, json_headers, testdata):
|
|
42
|
-
"""Test that anonymous cannot get any loan."""
|
|
43
|
-
loan_patron1 = testdata["loans"][0]
|
|
44
|
-
assert loan_patron1["patron_pid"] == "1"
|
|
45
|
-
loan_patron2 = testdata["loans"][3]
|
|
46
|
-
assert loan_patron2["patron_pid"] == "2"
|
|
47
|
-
|
|
48
|
-
res = _fetch_loan(client, json_headers, loan_patron1)
|
|
49
|
-
assert res.status_code == 401
|
|
50
|
-
res = _fetch_loan(client, json_headers, loan_patron2)
|
|
51
|
-
assert res.status_code == 401
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def test_admin_or_librarian_can_get_any_loan(client, json_headers, users, testdata):
|
|
55
|
-
"""Test that admins and librarians can get any loan."""
|
|
56
|
-
for username in ["admin", "librarian"]:
|
|
57
|
-
user_login(client, username, users)
|
|
58
|
-
# GET loans of Patron 1
|
|
59
|
-
loan_patron1 = testdata["loans"][0]
|
|
60
|
-
assert loan_patron1["patron_pid"] == "1"
|
|
61
|
-
_assert_get_loan_success(client, json_headers, loan_patron1)
|
|
62
|
-
|
|
63
|
-
# GET loans of Patron 2
|
|
64
|
-
loan_patron2 = testdata["loans"][3]
|
|
65
|
-
assert loan_patron2["patron_pid"] == "2"
|
|
66
|
-
_assert_get_loan_success(client, json_headers, loan_patron2)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def test_patron_can_get_only_his_loans(client, json_headers, users, testdata):
|
|
70
|
-
"""Test that patrons can get only their loans."""
|
|
71
|
-
loan_patron1 = testdata["loans"][0]
|
|
72
|
-
assert loan_patron1["patron_pid"] == "1"
|
|
73
|
-
|
|
74
|
-
loan_patron2 = testdata["loans"][3]
|
|
75
|
-
assert loan_patron2["patron_pid"] == "2"
|
|
76
|
-
|
|
77
|
-
# Patron 1 GET his loans
|
|
78
|
-
user_login(client, "patron1", users)
|
|
79
|
-
_assert_get_loan_success(client, json_headers, loan_patron1)
|
|
80
|
-
# Patron 1 GET loans of Patron 2
|
|
81
|
-
res = _fetch_loan(client, json_headers, loan_patron2)
|
|
82
|
-
assert res.status_code == 403
|
|
83
|
-
|
|
84
|
-
# Patron 2 GET his loans
|
|
85
|
-
user_login(client, "patron2", users)
|
|
86
|
-
_assert_get_loan_success(client, json_headers, loan_patron2)
|
|
87
|
-
# Patron 2 GET loans of Patron 1
|
|
88
|
-
res = _fetch_loan(client, json_headers, loan_patron1)
|
|
89
|
-
assert res.status_code == 403
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def _test_post_new_loan(client, json_headers, user_id, response_code):
|
|
93
|
-
"""Test POST new loan."""
|
|
94
|
-
url = url_for("invenio_records_rest.loanid_list")
|
|
95
|
-
params = deepcopy(NEW_LOAN)
|
|
96
|
-
params["transaction_user_pid"] = user_id
|
|
97
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
98
|
-
assert res.status_code == response_code
|
|
99
|
-
if res.status_code == 201:
|
|
100
|
-
data = json.loads(res.data.decode("utf-8"))["metadata"]
|
|
101
|
-
return data["pid"]
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def _test_replace_existing_loan(client, json_headers, user_id, loanid, response_code):
|
|
105
|
-
"""Test PUT existing loan."""
|
|
106
|
-
url = url_for("invenio_records_rest.loanid_item", pid_value=loanid)
|
|
107
|
-
params = deepcopy(NEW_LOAN)
|
|
108
|
-
params["transaction_user_pid"] = user_id
|
|
109
|
-
res = client.put(url, headers=json_headers, data=json.dumps(params))
|
|
110
|
-
assert res.status_code == response_code
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def _test_delete_existing_loan(client, json_headers, loanid, response_code):
|
|
114
|
-
"""Test DELETE existing loan."""
|
|
115
|
-
url = url_for("invenio_records_rest.loanid_item", pid_value=loanid)
|
|
116
|
-
res = client.delete(url, headers=json_headers)
|
|
117
|
-
assert res.status_code == response_code
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
def test_anonymous_cannot_update_loans(client, json_headers, testdata):
|
|
121
|
-
"""Test that anonymous cannot update loans."""
|
|
122
|
-
loanid = "loanid-1"
|
|
123
|
-
_test_post_new_loan(client, json_headers, user_id="", response_code=401)
|
|
124
|
-
_test_replace_existing_loan(
|
|
125
|
-
client, json_headers, user_id="", loanid=loanid, response_code=401
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
def test_patron_cannot_update_loans(client, json_headers, users, testdata):
|
|
130
|
-
"""Test that patrons cannot update loans."""
|
|
131
|
-
loanid = "loanid-1"
|
|
132
|
-
user = user_login(client, "patron1", users)
|
|
133
|
-
_test_post_new_loan(client, json_headers, user.id, 403)
|
|
134
|
-
_test_replace_existing_loan(client, json_headers, user.id, loanid, 403)
|
|
135
|
-
_test_delete_existing_loan(client, json_headers, loanid, 403)
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Copyright (C) 2019 CERN.
|
|
4
|
-
#
|
|
5
|
-
# Invenio-Circulation is free software; you can redistribute it and/or modify
|
|
6
|
-
# it under the terms of the MIT License; see LICENSE file for more details.
|
|
7
|
-
|
|
8
|
-
"""Tests for loan item resolver."""
|
|
9
|
-
|
|
10
|
-
from invenio_circulation.api import Loan
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def test_loan_item_resolver(app, testdata):
|
|
14
|
-
"""Test item resolving from loan."""
|
|
15
|
-
loan_pid = testdata["loans"][1]["pid"]
|
|
16
|
-
loan = Loan.get_record_by_pid(loan_pid)
|
|
17
|
-
loan = loan.replace_refs()
|
|
18
|
-
assert loan["item"]["pid"] == loan["item_pid"]["value"]
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def test_loan_item_resolver_for_empty_item_pid(app, testdata):
|
|
22
|
-
"""Test item resolving from loan."""
|
|
23
|
-
loan_pid = testdata["loans"][0]["pid"]
|
|
24
|
-
loan = Loan.get_record_by_pid(loan_pid)
|
|
25
|
-
loan = loan.replace_refs()
|
|
26
|
-
assert loan["item"] == dict()
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Copyright (C) 2018-2020 CERN.
|
|
4
|
-
#
|
|
5
|
-
# invenio-app-ils is free software; you can redistribute it and/or modify it
|
|
6
|
-
# under the terms of the MIT License; see LICENSE file for more details.
|
|
7
|
-
|
|
8
|
-
"""Test loans list permissions."""
|
|
9
|
-
|
|
10
|
-
import json
|
|
11
|
-
|
|
12
|
-
from flask import url_for
|
|
13
|
-
|
|
14
|
-
from tests.helpers import user_login, user_logout
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def _search_loans(client, json_headers, **kwargs):
|
|
18
|
-
"""Return the loan fetched with a REST call."""
|
|
19
|
-
url = url_for("invenio_records_rest.loanid_list", **kwargs)
|
|
20
|
-
return client.get(url, headers=json_headers)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def test_anonymous_cannot_search_any_loan(client, json_headers, users):
|
|
24
|
-
"""Test that anonymous cannot search any loan in search results."""
|
|
25
|
-
res = _search_loans(client, json_headers)
|
|
26
|
-
assert res.status_code == 401
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def test_admin_or_librarian_can_search_any_loan(client, json_headers, users, testdata):
|
|
30
|
-
"""Test that admins and librarians can search any loan."""
|
|
31
|
-
for username in ["admin", "librarian"]:
|
|
32
|
-
user_login(client, username, users)
|
|
33
|
-
res = _search_loans(client, json_headers)
|
|
34
|
-
assert res.status_code == 200
|
|
35
|
-
hits = json.loads(res.data.decode("utf-8"))
|
|
36
|
-
assert len(hits["hits"]["hits"]) == len(testdata["loans"])
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def test_patrons_can_search_their_own_loans(client, json_headers, users, testdata):
|
|
40
|
-
"""Test that patrons can search their own loans."""
|
|
41
|
-
|
|
42
|
-
def _validate_only_patron_loans(res, user, state):
|
|
43
|
-
"""Assert that result loans belong to the given user only."""
|
|
44
|
-
patron_loans = [
|
|
45
|
-
loan
|
|
46
|
-
for loan in testdata["loans"]
|
|
47
|
-
if loan["patron_pid"] == str(user.id) and loan["state"] == state
|
|
48
|
-
]
|
|
49
|
-
|
|
50
|
-
assert res.status_code == 200
|
|
51
|
-
hits = json.loads(res.data.decode("utf-8"))
|
|
52
|
-
assert len(hits["hits"]["hits"]) == len(patron_loans)
|
|
53
|
-
for hit in hits["hits"]["hits"]:
|
|
54
|
-
assert hit["metadata"]["patron_pid"] == str(user.id)
|
|
55
|
-
|
|
56
|
-
state = "PENDING"
|
|
57
|
-
for username in ["patron1", "patron2"]:
|
|
58
|
-
user = user_login(client, username, users)
|
|
59
|
-
# search with no params
|
|
60
|
-
res = _search_loans(client, json_headers, state=state)
|
|
61
|
-
_validate_only_patron_loans(res, user, state)
|
|
62
|
-
|
|
63
|
-
# search with params
|
|
64
|
-
res = _search_loans(
|
|
65
|
-
client,
|
|
66
|
-
json_headers,
|
|
67
|
-
state=state, # test extra query
|
|
68
|
-
q="patron_pid:{}".format(str(user.id)),
|
|
69
|
-
)
|
|
70
|
-
_validate_only_patron_loans(res, user, state)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def test_patrons_cannot_search_other_loans(client, json_headers, users, testdata):
|
|
74
|
-
"""Test that patrons cannot search for loans of other patrons."""
|
|
75
|
-
user_login(client, "patron1", users)
|
|
76
|
-
res = _search_loans(
|
|
77
|
-
client,
|
|
78
|
-
json_headers,
|
|
79
|
-
state="PENDING", # test extra query
|
|
80
|
-
q="patron_pid:{}".format(str(users["patron2"].id)),
|
|
81
|
-
)
|
|
82
|
-
assert res.status_code == 403
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def test_most_loaned_permissions(client, json_headers, users, testdata):
|
|
86
|
-
"""Test that only the backoffice is able to list the most loaned items."""
|
|
87
|
-
url = url_for("invenio_app_ils_circulation_stats.most-loaned")
|
|
88
|
-
tests = [
|
|
89
|
-
("admin", 200),
|
|
90
|
-
("librarian", 200),
|
|
91
|
-
("patron1", 403),
|
|
92
|
-
("anonymous", 401),
|
|
93
|
-
]
|
|
94
|
-
for username, status in tests:
|
|
95
|
-
user_login(client, username, users)
|
|
96
|
-
res = client.get(url, headers=json_headers)
|
|
97
|
-
assert res.status_code == status
|
|
98
|
-
user_logout(client)
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Copyright (C) 2019-2020 CERN.
|
|
4
|
-
#
|
|
5
|
-
# Invenio-Circulation is free software; you can redistribute it and/or modify
|
|
6
|
-
# it under the terms of the MIT License; see LICENSE file for more details.
|
|
7
|
-
|
|
8
|
-
"""Tests for loan patron resolver."""
|
|
9
|
-
|
|
10
|
-
from invenio_circulation.api import Loan
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def test_loan_patron_resolver(app, testdata):
|
|
14
|
-
"""Test item resolving from loan."""
|
|
15
|
-
loan_pid = testdata["loans"][0]["pid"]
|
|
16
|
-
loan = Loan.get_record_by_pid(loan_pid)
|
|
17
|
-
loan = loan.replace_refs()
|
|
18
|
-
assert loan["patron_pid"] == loan["patron"]["id"]
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def test_loan_patron_resolver_for_non_existing_patron_pid(app, testdata):
|
|
22
|
-
"""Test item resolving from loan."""
|
|
23
|
-
AnonymousPatron = app.config["ILS_PATRON_ANONYMOUS_CLASS"]
|
|
24
|
-
loan_pid = testdata["loans"][6]["pid"]
|
|
25
|
-
loan = Loan.get_record_by_pid(loan_pid)
|
|
26
|
-
loan = loan.replace_refs()
|
|
27
|
-
assert loan["patron_pid"] == str(20)
|
|
28
|
-
assert loan["patron"]["id"] == str(AnonymousPatron.id)
|
|
29
|
-
assert loan["patron"]["name"] == "anonymous"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def test_loan_patron_resolver_for_anonymous_patron_pid(app, testdata):
|
|
33
|
-
"""Test item resolving from loan."""
|
|
34
|
-
loan_pid = testdata["loans"][7]["pid"]
|
|
35
|
-
loan = Loan.get_record_by_pid(loan_pid)
|
|
36
|
-
loan = loan.replace_refs()
|
|
37
|
-
assert loan["patron_pid"] == loan["patron"]["id"]
|
|
38
|
-
assert loan["patron"]["name"] == "anonymous"
|
|
@@ -1,338 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Copyright (C) 2019-2020 CERN.
|
|
4
|
-
#
|
|
5
|
-
# invenio-app-ils is free software; you can redistribute it and/or modify it
|
|
6
|
-
# under the terms of the MIT License; see LICENSE file for more details.
|
|
7
|
-
|
|
8
|
-
"""Test loans item permissions."""
|
|
9
|
-
|
|
10
|
-
import json
|
|
11
|
-
from copy import deepcopy
|
|
12
|
-
from datetime import timedelta
|
|
13
|
-
|
|
14
|
-
import arrow
|
|
15
|
-
from flask import url_for
|
|
16
|
-
from invenio_search import current_search
|
|
17
|
-
|
|
18
|
-
from tests.helpers import user_login
|
|
19
|
-
|
|
20
|
-
NEW_LOAN = {
|
|
21
|
-
"document_pid": "CHANGE ME IN EACH TEST",
|
|
22
|
-
"patron_pid": "3",
|
|
23
|
-
"transaction_location_pid": "locid-1",
|
|
24
|
-
"pickup_location_pid": "locid-1",
|
|
25
|
-
"delivery": {"method": "PICKUP"},
|
|
26
|
-
"request_start_date": "2019-09-10",
|
|
27
|
-
"request_expire_date": "2019-09-20",
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
NEW_LOAN_2 = {
|
|
31
|
-
"document_pid": "CHANGE ME IN EACH TEST",
|
|
32
|
-
"patron_pid": "4",
|
|
33
|
-
"transaction_location_pid": "locid-4", # With Sat and Sun closures
|
|
34
|
-
"pickup_location_pid": "locid-4",
|
|
35
|
-
"delivery": {"method": "PICKUP"},
|
|
36
|
-
"request_start_date": "2024-03-15", # Friday
|
|
37
|
-
"request_expire_date": "CHANGE-MY-DATE",
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def test_anonymous_cannot_request_loan(client, json_headers, testdata):
|
|
42
|
-
"""Test that anonymous users cannot request a loan."""
|
|
43
|
-
url = url_for("invenio_app_ils_circulation.loan_request")
|
|
44
|
-
res = client.post(url, headers=json_headers, data=json.dumps(NEW_LOAN))
|
|
45
|
-
assert res.status_code == 401
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def test_patron_can_request_loan(client, json_headers, users, testdata):
|
|
49
|
-
"""Test that a patron can request a loan."""
|
|
50
|
-
url = url_for("invenio_app_ils_circulation.loan_request")
|
|
51
|
-
user = user_login(client, "patron1", users)
|
|
52
|
-
params = deepcopy(NEW_LOAN)
|
|
53
|
-
params["document_pid"] = "docid-3"
|
|
54
|
-
params["transaction_user_pid"] = str(user.id)
|
|
55
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
56
|
-
assert res.status_code == 202
|
|
57
|
-
loan = res.get_json()["metadata"]
|
|
58
|
-
assert loan["state"] == "PENDING"
|
|
59
|
-
assert loan["document_pid"] == params["document_pid"]
|
|
60
|
-
assert loan["transaction_date"]
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def test_patron_can_cancel_loan(client, json_headers, users, testdata, app_config):
|
|
64
|
-
"""Test that a patron can cancel its own loan."""
|
|
65
|
-
url = url_for("invenio_app_ils_circulation.loan_request")
|
|
66
|
-
user = user_login(client, "patron3", users)
|
|
67
|
-
params = deepcopy(NEW_LOAN)
|
|
68
|
-
params["document_pid"] = "docid-3"
|
|
69
|
-
params["transaction_user_pid"] = str(user.id)
|
|
70
|
-
|
|
71
|
-
# Create a Loan
|
|
72
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
73
|
-
loan = res.get_json()
|
|
74
|
-
|
|
75
|
-
cancel_url = loan["links"]["actions"]["cancel"]
|
|
76
|
-
meta = loan["metadata"]
|
|
77
|
-
payload = {
|
|
78
|
-
"document_pid": meta["document_pid"],
|
|
79
|
-
"patron_pid": meta["patron_pid"],
|
|
80
|
-
"transaction_location_pid": meta["transaction_location_pid"],
|
|
81
|
-
"transaction_user_pid": meta["transaction_user_pid"],
|
|
82
|
-
"cancel_reason": "USER_CANCEL",
|
|
83
|
-
}
|
|
84
|
-
assert res.status_code == 202
|
|
85
|
-
|
|
86
|
-
# Try to cancel a loan that belongs to patron3 as patron1
|
|
87
|
-
user_login(client, "patron1", users)
|
|
88
|
-
cancel_res = client.post(cancel_url, headers=json_headers, data=json.dumps(payload))
|
|
89
|
-
assert cancel_res.status_code == 403
|
|
90
|
-
|
|
91
|
-
# Try to cancel a loan that belongs to patron3 as patron3
|
|
92
|
-
user_login(client, "patron3", users)
|
|
93
|
-
cancel_res = client.post(cancel_url, headers=json_headers, data=json.dumps(payload))
|
|
94
|
-
assert cancel_res.status_code == 202
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def test_patron_can_request_loan_with_or_without_end_date(
|
|
98
|
-
app, client, json_headers, users, testdata
|
|
99
|
-
):
|
|
100
|
-
"""Test that a patron can request a loan [with/without] end date."""
|
|
101
|
-
url = url_for("invenio_app_ils_circulation.loan_request")
|
|
102
|
-
user = user_login(client, "patron1", users)
|
|
103
|
-
|
|
104
|
-
now = arrow.utcnow()
|
|
105
|
-
start_date = (now + timedelta(days=3)).date().isoformat()
|
|
106
|
-
end_date = (now + timedelta(days=15)).date().isoformat()
|
|
107
|
-
|
|
108
|
-
# it should succeed when start/end dates provided
|
|
109
|
-
params = deepcopy(NEW_LOAN)
|
|
110
|
-
params["document_pid"] = "docid-2"
|
|
111
|
-
params["request_start_date"] = start_date
|
|
112
|
-
params["request_expire_date"] = end_date
|
|
113
|
-
params["transaction_user_pid"] = str(user.id)
|
|
114
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
115
|
-
assert res.status_code == 202
|
|
116
|
-
loan = res.get_json()["metadata"]
|
|
117
|
-
assert loan["state"] == "PENDING"
|
|
118
|
-
assert loan["document_pid"] == params["document_pid"]
|
|
119
|
-
assert loan["request_start_date"] == start_date
|
|
120
|
-
assert loan["request_expire_date"] == end_date
|
|
121
|
-
assert loan["transaction_date"]
|
|
122
|
-
|
|
123
|
-
# it should fail when wrong date provided
|
|
124
|
-
params = deepcopy(NEW_LOAN)
|
|
125
|
-
past_end_date = now - timedelta(days=30)
|
|
126
|
-
params["request_expire_date"] = past_end_date.date().isoformat()
|
|
127
|
-
params["transaction_user_pid"] = str(user.id)
|
|
128
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
129
|
-
assert res.status_code == 400
|
|
130
|
-
|
|
131
|
-
# it should fail when request duration over max
|
|
132
|
-
params = deepcopy(NEW_LOAN)
|
|
133
|
-
days = app.config["ILS_CIRCULATION_LOAN_REQUEST_DURATION_DAYS"]
|
|
134
|
-
past_end_date = now + timedelta(days=days + 1)
|
|
135
|
-
params["request_expire_date"] = past_end_date.date().isoformat()
|
|
136
|
-
params["transaction_user_pid"] = str(user.id)
|
|
137
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
138
|
-
assert res.status_code == 400
|
|
139
|
-
|
|
140
|
-
# it should fail when request start/end date not provided
|
|
141
|
-
params = deepcopy(NEW_LOAN)
|
|
142
|
-
params["document_pid"] = "docid-4"
|
|
143
|
-
params["transaction_user_pid"] = str(user.id)
|
|
144
|
-
del params["request_start_date"]
|
|
145
|
-
del params["request_expire_date"]
|
|
146
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
147
|
-
assert res.status_code == 202
|
|
148
|
-
loan = res.get_json()["metadata"]
|
|
149
|
-
now = arrow.utcnow()
|
|
150
|
-
start_date = now.date().isoformat()
|
|
151
|
-
days = app.config["ILS_CIRCULATION_LOAN_REQUEST_DURATION_DAYS"]
|
|
152
|
-
end_date = (now + timedelta(days=days)).date().isoformat()
|
|
153
|
-
assert loan["request_start_date"] == start_date
|
|
154
|
-
assert loan["request_expire_date"] == end_date
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def test_request_loan_with_or_without_delivery(
|
|
158
|
-
app, client, json_headers, users, testdata
|
|
159
|
-
):
|
|
160
|
-
"""Test that loan request with or without delivery."""
|
|
161
|
-
url = url_for("invenio_app_ils_circulation.loan_request")
|
|
162
|
-
user = user_login(client, "patron1", users)
|
|
163
|
-
|
|
164
|
-
previous_dev_methods = app.config["ILS_CIRCULATION_DELIVERY_METHODS"]
|
|
165
|
-
app.config["ILS_CIRCULATION_DELIVERY_METHODS"] = {}
|
|
166
|
-
params = deepcopy(NEW_LOAN)
|
|
167
|
-
params["document_pid"] = "docid-12"
|
|
168
|
-
params["transaction_user_pid"] = str(user.id)
|
|
169
|
-
del params["delivery"]
|
|
170
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
171
|
-
assert res.status_code == 202
|
|
172
|
-
|
|
173
|
-
app.config["ILS_CIRCULATION_DELIVERY_METHODS"] = {
|
|
174
|
-
"TEST_METHOD1": "",
|
|
175
|
-
"TEST_METHOD2": "",
|
|
176
|
-
}
|
|
177
|
-
params = deepcopy(NEW_LOAN)
|
|
178
|
-
params["document_pid"] = "docid-6"
|
|
179
|
-
params["delivery"] = {"method": "TEST_METHOD1"}
|
|
180
|
-
params["transaction_user_pid"] = str(user.id)
|
|
181
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
182
|
-
assert res.status_code == 202
|
|
183
|
-
|
|
184
|
-
app.config["ILS_CIRCULATION_DELIVERY_METHODS"] = {"TEST_METHOD": ""}
|
|
185
|
-
params = deepcopy(NEW_LOAN)
|
|
186
|
-
params["transaction_user_pid"] = str(user.id)
|
|
187
|
-
del params["delivery"]
|
|
188
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
189
|
-
assert res.status_code == 400
|
|
190
|
-
assert res.get_json()["message"] == "Validation error."
|
|
191
|
-
|
|
192
|
-
app.config["ILS_CIRCULATION_DELIVERY_METHODS"] = {"TEST_METHOD": ""}
|
|
193
|
-
params = deepcopy(NEW_LOAN)
|
|
194
|
-
params["delivery"] = {"method": "NON_EXISTING_METHOD"}
|
|
195
|
-
params["transaction_user_pid"] = str(user.id)
|
|
196
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
197
|
-
assert res.status_code == 400
|
|
198
|
-
assert res.get_json()["message"] == "Validation error."
|
|
199
|
-
|
|
200
|
-
# restore
|
|
201
|
-
app.config["ILS_CIRCULATION_DELIVERY_METHODS"] = previous_dev_methods
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
def test_request_loan_with_active_loan_or_loan_request(
|
|
205
|
-
app, client, json_headers, users, testdata
|
|
206
|
-
):
|
|
207
|
-
"""Test that a patron cannot request a loan with already an active loan or
|
|
208
|
-
a loan request on the same document.
|
|
209
|
-
"""
|
|
210
|
-
# Try to request document with already a loan request
|
|
211
|
-
url = url_for("invenio_app_ils_circulation.loan_request")
|
|
212
|
-
user = user_login(client, "patron1", users)
|
|
213
|
-
params = deepcopy(NEW_LOAN)
|
|
214
|
-
params["document_pid"] = "docid-3"
|
|
215
|
-
params["transaction_user_pid"] = str(user.id)
|
|
216
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
217
|
-
assert res.status_code == 202
|
|
218
|
-
current_search.flush_and_refresh(index="*")
|
|
219
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
220
|
-
assert res.status_code == 400
|
|
221
|
-
|
|
222
|
-
# Try to request document with already an active loan
|
|
223
|
-
user_login(client, "librarian", users)
|
|
224
|
-
url = url_for("invenio_app_ils_circulation.loan_checkout")
|
|
225
|
-
params = deepcopy(NEW_LOAN)
|
|
226
|
-
params["document_pid"] = "docid-3"
|
|
227
|
-
params["transaction_user_pid"] = str(users["librarian"].id)
|
|
228
|
-
params["item_pid"] = dict(type="pitmid", value="itemid-60")
|
|
229
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
230
|
-
assert res.status_code == 202
|
|
231
|
-
current_search.flush_and_refresh(index="*")
|
|
232
|
-
url = url_for("invenio_app_ils_circulation.loan_request")
|
|
233
|
-
user = user_login(client, "patron1", users)
|
|
234
|
-
params = deepcopy(NEW_LOAN)
|
|
235
|
-
params["document_pid"] = "docid-3"
|
|
236
|
-
params["transaction_user_pid"] = str(user.id)
|
|
237
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
238
|
-
assert res.status_code == 400
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
def test_request_loan_minimum_days(app, client, json_headers, users, testdata):
|
|
242
|
-
"""Test that a patron can only request a loan after a set minimum days"""
|
|
243
|
-
url = url_for("invenio_app_ils_circulation.loan_request")
|
|
244
|
-
user = user_login(client, "patron1", users)
|
|
245
|
-
app.config["ILS_CIRCULATION_LOAN_REQUEST_OFFSET"] = 4
|
|
246
|
-
params = deepcopy(NEW_LOAN)
|
|
247
|
-
|
|
248
|
-
now = arrow.utcnow()
|
|
249
|
-
start_date = now.date().isoformat()
|
|
250
|
-
params["request_start_date"] = start_date
|
|
251
|
-
params["transaction_user_pid"] = str(user.id)
|
|
252
|
-
|
|
253
|
-
params["document_pid"] = "docid-4"
|
|
254
|
-
end_date = (now + timedelta(days=2)).date().isoformat() # FAIL
|
|
255
|
-
params["request_expire_date"] = end_date
|
|
256
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
257
|
-
assert res.status_code == 400
|
|
258
|
-
assert res.get_json()["message"] == "Validation error."
|
|
259
|
-
|
|
260
|
-
params["document_pid"] = "docid-5"
|
|
261
|
-
end_date = (now - timedelta(days=2)).date().isoformat() # FAIL
|
|
262
|
-
params["request_expire_date"] = end_date
|
|
263
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
264
|
-
assert res.status_code == 400
|
|
265
|
-
assert res.get_json()["message"] == "Validation error."
|
|
266
|
-
|
|
267
|
-
params["document_pid"] = "docid-6"
|
|
268
|
-
end_date = now.date().isoformat() # FAIL
|
|
269
|
-
params["request_expire_date"] = end_date
|
|
270
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
271
|
-
assert res.status_code == 400
|
|
272
|
-
assert res.get_json()["message"] == "Validation error."
|
|
273
|
-
|
|
274
|
-
params["document_pid"] = "docid-7"
|
|
275
|
-
end_date = (now + timedelta(days=4)).date().isoformat() # PASS
|
|
276
|
-
params["request_expire_date"] = end_date
|
|
277
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
278
|
-
assert res.status_code == 202
|
|
279
|
-
loan = res.get_json()["metadata"]
|
|
280
|
-
assert loan["state"] == "PENDING"
|
|
281
|
-
assert loan["document_pid"] == params["document_pid"]
|
|
282
|
-
assert loan["transaction_date"]
|
|
283
|
-
|
|
284
|
-
params["document_pid"] = "docid-8"
|
|
285
|
-
end_date = (now + timedelta(days=8)).date().isoformat() # PASS
|
|
286
|
-
params["request_expire_date"] = end_date
|
|
287
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
288
|
-
assert res.status_code == 202
|
|
289
|
-
loan = res.get_json()["metadata"]
|
|
290
|
-
assert loan["state"] == "PENDING"
|
|
291
|
-
assert loan["document_pid"] == params["document_pid"]
|
|
292
|
-
assert loan["transaction_date"]
|
|
293
|
-
|
|
294
|
-
app.config["ILS_CIRCULATION_LOAN_REQUEST_OFFSET"] = -3
|
|
295
|
-
|
|
296
|
-
params["document_pid"] = "docid-9"
|
|
297
|
-
end_date = now.date().isoformat() # FAIL
|
|
298
|
-
params["request_expire_date"] = end_date
|
|
299
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
300
|
-
assert res.status_code == 400
|
|
301
|
-
assert res.get_json()["message"] == "Validation error."
|
|
302
|
-
|
|
303
|
-
params["document_pid"] = "docid-10"
|
|
304
|
-
end_date = (now - timedelta(days=2)).date().isoformat() # FAIL
|
|
305
|
-
params["request_expire_date"] = end_date
|
|
306
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
307
|
-
assert res.status_code == 400
|
|
308
|
-
assert res.get_json()["message"] == "Validation error."
|
|
309
|
-
|
|
310
|
-
app.config["ILS_CIRCULATION_LOAN_REQUEST_OFFSET"] = 2
|
|
311
|
-
# By-pass default location validation for testing closures on a different location
|
|
312
|
-
location_validator, app.config["CIRCULATION_TRANSACTION_LOCATION_VALIDATOR"] = (
|
|
313
|
-
app.config["CIRCULATION_TRANSACTION_LOCATION_VALIDATOR"],
|
|
314
|
-
lambda _: True,
|
|
315
|
-
)
|
|
316
|
-
# Request Date is Friday
|
|
317
|
-
params = deepcopy(NEW_LOAN_2)
|
|
318
|
-
params["transaction_user_pid"] = str(user.id)
|
|
319
|
-
|
|
320
|
-
params["document_pid"] = "docid-11"
|
|
321
|
-
# Monday
|
|
322
|
-
params["request_expire_date"] = "2024-03-18" # FAIL
|
|
323
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
324
|
-
assert res.status_code == 400
|
|
325
|
-
assert res.get_json()["message"] == "Validation error."
|
|
326
|
-
|
|
327
|
-
params["document_pid"] = "docid-12"
|
|
328
|
-
# Tuesday
|
|
329
|
-
params["request_expire_date"] = "2024-03-19" # PASS
|
|
330
|
-
res = client.post(url, headers=json_headers, data=json.dumps(params))
|
|
331
|
-
assert res.status_code == 202
|
|
332
|
-
loan = res.get_json()["metadata"]
|
|
333
|
-
assert loan["state"] == "PENDING"
|
|
334
|
-
assert loan["document_pid"] == params["document_pid"]
|
|
335
|
-
assert loan["transaction_date"]
|
|
336
|
-
|
|
337
|
-
# Set the config back to its original state
|
|
338
|
-
app.config["CIRCULATION_TRANSACTION_LOCATION_VALIDATOR"] = location_validator
|