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.
Files changed (115) hide show
  1. invenio_app_ils/__init__.py +1 -1
  2. invenio_app_ils/assets/semantic-ui/less/theme.config +103 -0
  3. invenio_app_ils/circulation/api.py +9 -5
  4. invenio_app_ils/circulation/config.py +1 -0
  5. invenio_app_ils/circulation/loaders/schemas/json/loan_checkout.py +1 -1
  6. invenio_app_ils/circulation/loaders/schemas/json/loan_request.py +2 -2
  7. invenio_app_ils/circulation/notifications/api.py +9 -1
  8. invenio_app_ils/circulation/notifications/messages.py +2 -1
  9. invenio_app_ils/circulation/templates/invenio_app_ils_circulation/notifications/update_dates.html +19 -0
  10. invenio_app_ils/cli.py +92 -64
  11. invenio_app_ils/config.py +10 -2
  12. invenio_app_ils/documents/loaders/jsonschemas/document.py +1 -1
  13. invenio_app_ils/eitems/loaders/jsonschemas/eitems.py +2 -2
  14. invenio_app_ils/patrons/anonymization.py +0 -4
  15. invenio_app_ils/series/loaders/jsonschemas/series.py +1 -1
  16. invenio_app_ils/webpack.py +27 -0
  17. {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/METADATA +52 -42
  18. {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/RECORD +24 -112
  19. {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/entry_points.txt +3 -0
  20. {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/top_level.txt +0 -1
  21. invenio_app_ils/notifications/admin.py +0 -57
  22. tests/__init__.py +0 -8
  23. tests/api/__init__.py +0 -8
  24. tests/api/acquisition/__init__.py +0 -8
  25. tests/api/acquisition/test_acq_orders_crud.py +0 -99
  26. tests/api/acquisition/test_acq_orders_permissions.py +0 -104
  27. tests/api/acquisition/test_acq_providers_permissions.py +0 -83
  28. tests/api/circulation/__init__.py +0 -8
  29. tests/api/circulation/test_expired_loans.py +0 -88
  30. tests/api/circulation/test_loan_bulk_extend.py +0 -169
  31. tests/api/circulation/test_loan_checkout.py +0 -422
  32. tests/api/circulation/test_loan_default_durations.py +0 -43
  33. tests/api/circulation/test_loan_document_resolver.py +0 -18
  34. tests/api/circulation/test_loan_extend.py +0 -122
  35. tests/api/circulation/test_loan_item_permissions.py +0 -135
  36. tests/api/circulation/test_loan_item_resolver.py +0 -26
  37. tests/api/circulation/test_loan_list_permissions.py +0 -98
  38. tests/api/circulation/test_loan_patron_resolver.py +0 -38
  39. tests/api/circulation/test_loan_request.py +0 -338
  40. tests/api/circulation/test_loan_update.py +0 -150
  41. tests/api/circulation/test_notifications.py +0 -221
  42. tests/api/conftest.py +0 -227
  43. tests/api/document_requests/__init__.py +0 -8
  44. tests/api/document_requests/test_document_requests.py +0 -131
  45. tests/api/document_requests/test_document_requests_permissions.py +0 -159
  46. tests/api/document_requests/test_notifications_filter.py +0 -35
  47. tests/api/ill/__init__.py +0 -8
  48. tests/api/ill/test_ill_brw_crud.py +0 -74
  49. tests/api/ill/test_ill_brw_reqs_patron_loan_create_action.py +0 -198
  50. tests/api/ill/test_ill_brw_reqs_patron_loan_extension_actions.py +0 -280
  51. tests/api/ill/test_ill_brw_reqs_permissions.py +0 -163
  52. tests/api/ill/test_ill_providers_permissions.py +0 -82
  53. tests/api/ils/__init__.py +0 -8
  54. tests/api/ils/documents/__init__.py +0 -8
  55. tests/api/ils/documents/test_document_crud.py +0 -57
  56. tests/api/ils/documents/test_document_permissions.py +0 -100
  57. tests/api/ils/documents/test_document_resolvers.py +0 -35
  58. tests/api/ils/eitems/__init__.py +0 -8
  59. tests/api/ils/eitems/test_eitems_crud.py +0 -42
  60. tests/api/ils/eitems/test_eitems_permissions.py +0 -85
  61. tests/api/ils/eitems/test_files.py +0 -162
  62. tests/api/ils/items/__init__.py +0 -8
  63. tests/api/ils/items/test_apis.py +0 -43
  64. tests/api/ils/items/test_items_crud.py +0 -99
  65. tests/api/ils/items/test_items_permissions.py +0 -107
  66. tests/api/ils/records_relations/__init__.py +0 -8
  67. tests/api/ils/records_relations/helpers.py +0 -115
  68. tests/api/ils/records_relations/test_records_relations_parentchild.py +0 -560
  69. tests/api/ils/records_relations/test_records_relations_sequence.py +0 -294
  70. tests/api/ils/records_relations/test_records_relations_siblings.py +0 -751
  71. tests/api/ils/series/__init__.py +0 -8
  72. tests/api/ils/series/test_series_permissions.py +0 -95
  73. tests/api/ils/test_anonymization.py +0 -181
  74. tests/api/ils/test_apis.py +0 -76
  75. tests/api/ils/test_closures.py +0 -353
  76. tests/api/ils/test_errors.py +0 -125
  77. tests/api/ils/test_facets.py +0 -88
  78. tests/api/ils/test_internal_locations.py +0 -96
  79. tests/api/ils/test_loaders.py +0 -51
  80. tests/api/ils/test_metadata_extensions.py +0 -206
  81. tests/api/ils/test_notifications.py +0 -173
  82. tests/api/ils/test_notifications_mails.py +0 -37
  83. tests/api/ils/test_notifications_permissions.py +0 -55
  84. tests/api/ils/test_patrons.py +0 -102
  85. tests/api/ils/test_record_delete.py +0 -42
  86. tests/api/ils/test_record_permissions.py +0 -132
  87. tests/api/ils/test_resolvers.py +0 -205
  88. tests/api/ils/test_stats.py +0 -142
  89. tests/api/ils/test_tasks.py +0 -209
  90. tests/api/ils/test_vocabularies.py +0 -35
  91. tests/conftest.py +0 -221
  92. tests/data/acq_orders.json +0 -110
  93. tests/data/acq_providers.json +0 -12
  94. tests/data/document_requests.json +0 -77
  95. tests/data/documents.json +0 -238
  96. tests/data/eitems.json +0 -71
  97. tests/data/ill_borrowing_requests.json +0 -77
  98. tests/data/ill_providers.json +0 -12
  99. tests/data/internal_locations.json +0 -22
  100. tests/data/items.json +0 -304
  101. tests/data/loans.json +0 -133
  102. tests/data/loans_most_loaned.json +0 -128
  103. tests/data/locations.json +0 -26
  104. tests/data/series.json +0 -33
  105. tests/helpers.py +0 -107
  106. tests/templates/notifications/title_body.html +0 -8
  107. tests/templates/notifications/title_body_html.html +0 -13
  108. tests/templates/notifications/title_body_html_ctx.html +0 -13
  109. tests/templates/notifications/title_only.html +0 -3
  110. tests/test_post_logout_redirect.py +0 -23
  111. tests/test_version.py +0 -17
  112. /tests/templates/notifications/blank.html → /invenio_app_ils/assets/semantic-ui/templates/.gitkeep +0 -0
  113. {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/WHEEL +0 -0
  114. {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/licenses/AUTHORS.rst +0 -0
  115. {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