invenio-app-ils 4.2.0__py2.py3-none-any.whl → 4.4.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/circulation/api.py +123 -26
- invenio_app_ils/circulation/config.py +14 -2
- invenio_app_ils/circulation/loaders/__init__.py +2 -0
- invenio_app_ils/circulation/loaders/schemas/json/loan_self_checkout.py +19 -0
- invenio_app_ils/circulation/notifications/messages.py +1 -0
- invenio_app_ils/circulation/serializers/__init__.py +1 -0
- invenio_app_ils/circulation/serializers/response.py +1 -0
- invenio_app_ils/circulation/templates/invenio_app_ils_circulation/notifications/self_checkout.html +19 -0
- invenio_app_ils/circulation/views.py +100 -12
- invenio_app_ils/errors.py +103 -4
- invenio_app_ils/items/search.py +13 -0
- invenio_app_ils/patrons/anonymization.py +39 -9
- invenio_app_ils/permissions.py +10 -5
- {invenio_app_ils-4.2.0.dist-info → invenio_app_ils-4.4.0.dist-info}/METADATA +24 -12
- {invenio_app_ils-4.2.0.dist-info → invenio_app_ils-4.4.0.dist-info}/RECORD +23 -21
- {invenio_app_ils-4.2.0.dist-info → invenio_app_ils-4.4.0.dist-info}/WHEEL +1 -1
- {invenio_app_ils-4.2.0.dist-info → invenio_app_ils-4.4.0.dist-info}/entry_points.txt +0 -1
- tests/api/circulation/test_loan_checkout.py +171 -66
- tests/data/items.json +49 -27
- {invenio_app_ils-4.2.0.dist-info → invenio_app_ils-4.4.0.dist-info}/AUTHORS.rst +0 -0
- {invenio_app_ils-4.2.0.dist-info → invenio_app_ils-4.4.0.dist-info}/LICENSE +0 -0
- {invenio_app_ils-4.2.0.dist-info → invenio_app_ils-4.4.0.dist-info}/top_level.txt +0 -0
invenio_app_ils/__init__.py
CHANGED
|
@@ -30,10 +30,15 @@ from invenio_app_ils.circulation.search import (
|
|
|
30
30
|
get_all_expiring_or_overdue_loans_by_patron_pid,
|
|
31
31
|
)
|
|
32
32
|
from invenio_app_ils.errors import (
|
|
33
|
+
DocumentOverbookedError,
|
|
33
34
|
IlsException,
|
|
34
35
|
InvalidLoanExtendError,
|
|
35
36
|
InvalidParameterError,
|
|
37
|
+
ItemCannotCirculateError,
|
|
38
|
+
ItemHasActiveLoanError,
|
|
39
|
+
ItemNotFoundError,
|
|
36
40
|
MissingRequiredParameterError,
|
|
41
|
+
MultipleItemsBarcodeFoundError,
|
|
37
42
|
PatronHasLoanOnDocumentError,
|
|
38
43
|
PatronHasLoanOnItemError,
|
|
39
44
|
PatronHasRequestOnDocumentError,
|
|
@@ -119,7 +124,7 @@ def request_loan(
|
|
|
119
124
|
patron_pid,
|
|
120
125
|
transaction_location_pid,
|
|
121
126
|
transaction_user_pid=None,
|
|
122
|
-
**kwargs
|
|
127
|
+
**kwargs,
|
|
123
128
|
):
|
|
124
129
|
"""Create a new loan and trigger the first transition to PENDING."""
|
|
125
130
|
loan_cls = current_circulation.loan_record_cls
|
|
@@ -170,13 +175,54 @@ def patron_has_active_loan_on_item(patron_pid, item_pid):
|
|
|
170
175
|
return search_result.hits.total.value > 0
|
|
171
176
|
|
|
172
177
|
|
|
178
|
+
def _checkout_loan(
|
|
179
|
+
item_pid,
|
|
180
|
+
patron_pid,
|
|
181
|
+
transaction_location_pid,
|
|
182
|
+
trigger="checkout",
|
|
183
|
+
transaction_user_pid=None,
|
|
184
|
+
delivery=None,
|
|
185
|
+
**kwargs,
|
|
186
|
+
):
|
|
187
|
+
"""Checkout a loan."""
|
|
188
|
+
transaction_user_pid = transaction_user_pid or str(current_user.id)
|
|
189
|
+
loan_cls = current_circulation.loan_record_cls
|
|
190
|
+
# create a new loan
|
|
191
|
+
record_uuid = uuid.uuid4()
|
|
192
|
+
new_loan = dict(
|
|
193
|
+
patron_pid=patron_pid,
|
|
194
|
+
transaction_location_pid=transaction_location_pid,
|
|
195
|
+
transaction_user_pid=transaction_user_pid,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
if delivery:
|
|
199
|
+
new_loan["delivery"] = delivery
|
|
200
|
+
# check if there is an existing request
|
|
201
|
+
loan = patron_has_request_on_document(patron_pid, kwargs.get("document_pid"))
|
|
202
|
+
if loan:
|
|
203
|
+
loan = loan_cls.get_record_by_pid(loan.pid)
|
|
204
|
+
pid = IlsCirculationLoanIdProvider.get(loan["pid"]).pid
|
|
205
|
+
loan.update(new_loan)
|
|
206
|
+
else:
|
|
207
|
+
pid = ils_circulation_loan_pid_minter(record_uuid, data=new_loan)
|
|
208
|
+
loan = loan_cls.create(data=new_loan, id_=record_uuid)
|
|
209
|
+
|
|
210
|
+
params = deepcopy(loan)
|
|
211
|
+
params.update(item_pid=item_pid, **kwargs)
|
|
212
|
+
|
|
213
|
+
loan = current_circulation.circulation.trigger(
|
|
214
|
+
loan, **dict(params, trigger=trigger)
|
|
215
|
+
)
|
|
216
|
+
return pid, loan
|
|
217
|
+
|
|
218
|
+
|
|
173
219
|
def checkout_loan(
|
|
174
220
|
item_pid,
|
|
175
221
|
patron_pid,
|
|
176
222
|
transaction_location_pid,
|
|
177
223
|
transaction_user_pid=None,
|
|
178
224
|
force=False,
|
|
179
|
-
**kwargs
|
|
225
|
+
**kwargs,
|
|
180
226
|
):
|
|
181
227
|
"""Create a new loan and trigger the first transition to ITEM_ON_LOAN.
|
|
182
228
|
|
|
@@ -191,7 +237,7 @@ def checkout_loan(
|
|
|
191
237
|
the checkout. If False, the checkout will fail when the item cannot
|
|
192
238
|
circulate.
|
|
193
239
|
"""
|
|
194
|
-
|
|
240
|
+
|
|
195
241
|
if patron_has_active_loan_on_item(patron_pid=patron_pid, item_pid=item_pid):
|
|
196
242
|
raise PatronHasLoanOnItemError(patron_pid, item_pid)
|
|
197
243
|
optional_delivery = kwargs.get("delivery")
|
|
@@ -201,35 +247,86 @@ def checkout_loan(
|
|
|
201
247
|
if force:
|
|
202
248
|
_set_item_to_can_circulate(item_pid)
|
|
203
249
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
new_loan = dict(
|
|
209
|
-
patron_pid=patron_pid,
|
|
210
|
-
transaction_location_pid=transaction_location_pid,
|
|
250
|
+
return _checkout_loan(
|
|
251
|
+
item_pid,
|
|
252
|
+
patron_pid,
|
|
253
|
+
transaction_location_pid,
|
|
211
254
|
transaction_user_pid=transaction_user_pid,
|
|
255
|
+
**kwargs,
|
|
212
256
|
)
|
|
213
257
|
|
|
214
|
-
# check if there is an existing request
|
|
215
|
-
loan = patron_has_request_on_document(patron_pid, kwargs.get("document_pid"))
|
|
216
|
-
if loan:
|
|
217
|
-
loan = loan_cls.get_record_by_pid(loan.pid)
|
|
218
|
-
pid = IlsCirculationLoanIdProvider.get(loan["pid"]).pid
|
|
219
|
-
loan.update(new_loan)
|
|
220
|
-
else:
|
|
221
|
-
pid = ils_circulation_loan_pid_minter(record_uuid, data=new_loan)
|
|
222
|
-
loan = loan_cls.create(data=new_loan, id_=record_uuid)
|
|
223
258
|
|
|
224
|
-
|
|
225
|
-
|
|
259
|
+
def _ensure_item_loanable_via_self_checkout(item_pid):
|
|
260
|
+
"""Self-checkout: return loanable item or raise when not loanable.
|
|
226
261
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
262
|
+
Implements the self-checkout rules to loan an item.
|
|
263
|
+
"""
|
|
264
|
+
item = current_app_ils.item_record_cls.get_record_by_pid(item_pid)
|
|
265
|
+
item_dict = item.replace_refs()
|
|
266
|
+
|
|
267
|
+
if item_dict["status"] != "CAN_CIRCULATE":
|
|
268
|
+
raise ItemCannotCirculateError()
|
|
269
|
+
|
|
270
|
+
circulation_state = item_dict["circulation"].get("state")
|
|
271
|
+
has_active_loan = (
|
|
272
|
+
circulation_state and circulation_state in CIRCULATION_STATES_LOAN_ACTIVE
|
|
230
273
|
)
|
|
274
|
+
if has_active_loan:
|
|
275
|
+
raise ItemHasActiveLoanError(loan_pid=item_dict["circulation"]["loan_pid"])
|
|
231
276
|
|
|
232
|
-
|
|
277
|
+
document = current_app_ils.document_record_cls.get_record_by_pid(
|
|
278
|
+
item_dict["document_pid"]
|
|
279
|
+
)
|
|
280
|
+
document_dict = document.replace_refs()
|
|
281
|
+
if document_dict["circulation"].get("overbooked", False):
|
|
282
|
+
raise DocumentOverbookedError(
|
|
283
|
+
f"Cannot self-checkout the overbooked document {item_dict['document_pid']}"
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
return item
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def self_checkout_get_item_by_barcode(barcode):
|
|
290
|
+
"""Search for an item by barcode.
|
|
291
|
+
|
|
292
|
+
:param barcode: the barcode of the item to search for
|
|
293
|
+
:return item: the item that was found, or raise in case of errors
|
|
294
|
+
"""
|
|
295
|
+
item_search = current_app_ils.item_search_cls()
|
|
296
|
+
items = item_search.search_by_barcode(barcode).execute()
|
|
297
|
+
if items.hits.total.value == 0:
|
|
298
|
+
raise ItemNotFoundError(barcode=barcode)
|
|
299
|
+
if items.hits.total.value > 1:
|
|
300
|
+
raise MultipleItemsBarcodeFoundError(barcode)
|
|
301
|
+
|
|
302
|
+
item_pid = items.hits[0].pid
|
|
303
|
+
item = _ensure_item_loanable_via_self_checkout(item_pid)
|
|
304
|
+
return item_pid, item
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def self_checkout(
|
|
308
|
+
item_pid, patron_pid, transaction_location_pid, transaction_user_pid=None, **kwargs
|
|
309
|
+
):
|
|
310
|
+
"""Perform self-checkout.
|
|
311
|
+
|
|
312
|
+
:param item_pid: a dict containing `value` and `type` fields to
|
|
313
|
+
uniquely identify the item.
|
|
314
|
+
:param patron_pid: the PID value of the patron
|
|
315
|
+
:param transaction_location_pid: the PID value of the location where the
|
|
316
|
+
checkout is performed
|
|
317
|
+
:param transaction_user_pid: the PID value of the user that performed the
|
|
318
|
+
checkout
|
|
319
|
+
"""
|
|
320
|
+
_ensure_item_loanable_via_self_checkout(item_pid["value"])
|
|
321
|
+
return _checkout_loan(
|
|
322
|
+
item_pid,
|
|
323
|
+
patron_pid,
|
|
324
|
+
transaction_location_pid,
|
|
325
|
+
transaction_user_pid=transaction_user_pid,
|
|
326
|
+
trigger="self_checkout",
|
|
327
|
+
delivery=dict(method="SELF-CHECKOUT"),
|
|
328
|
+
**kwargs,
|
|
329
|
+
)
|
|
233
330
|
|
|
234
331
|
|
|
235
332
|
def bulk_extend_loans(patron_pid, **kwargs):
|
|
@@ -253,7 +350,7 @@ def bulk_extend_loans(patron_pid, **kwargs):
|
|
|
253
350
|
params,
|
|
254
351
|
trigger="extend",
|
|
255
352
|
transition_kwargs=dict(send_notification=False),
|
|
256
|
-
)
|
|
353
|
+
),
|
|
257
354
|
)
|
|
258
355
|
extended_loans.append(extended_loan)
|
|
259
356
|
except (CirculationException, InvalidLoanExtendError):
|
|
@@ -44,7 +44,6 @@ from invenio_app_ils.permissions import (
|
|
|
44
44
|
PatronOwnerPermission,
|
|
45
45
|
authenticated_user_permission,
|
|
46
46
|
backoffice_permission,
|
|
47
|
-
loan_checkout_permission,
|
|
48
47
|
loan_extend_circulation_permission,
|
|
49
48
|
patron_owner_permission,
|
|
50
49
|
superuser_permission,
|
|
@@ -84,6 +83,7 @@ ILS_CIRCULATION_LOAN_WILL_EXPIRE_DAYS = 7
|
|
|
84
83
|
ILS_CIRCULATION_DELIVERY_METHODS = {
|
|
85
84
|
"PICKUP": "Pick it up at the library desk",
|
|
86
85
|
"DELIVERY": "Have it delivered to my office",
|
|
86
|
+
"SELF-CHECKOUT": "Self-checkout",
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
# Notification message creator for loan notifications
|
|
@@ -162,7 +162,13 @@ CIRCULATION_LOAN_TRANSITIONS = {
|
|
|
162
162
|
dest="ITEM_ON_LOAN",
|
|
163
163
|
trigger="checkout",
|
|
164
164
|
transition=ILSToItemOnLoan,
|
|
165
|
-
permission_factory=
|
|
165
|
+
permission_factory=backoffice_permission,
|
|
166
|
+
),
|
|
167
|
+
dict(
|
|
168
|
+
dest="ITEM_ON_LOAN",
|
|
169
|
+
trigger="self_checkout",
|
|
170
|
+
transition=ILSToItemOnLoan,
|
|
171
|
+
permission_factory=authenticated_user_permission,
|
|
166
172
|
),
|
|
167
173
|
],
|
|
168
174
|
"PENDING": [
|
|
@@ -172,6 +178,12 @@ CIRCULATION_LOAN_TRANSITIONS = {
|
|
|
172
178
|
transition=ILSToItemOnLoan,
|
|
173
179
|
permission_factory=backoffice_permission,
|
|
174
180
|
),
|
|
181
|
+
dict(
|
|
182
|
+
dest="ITEM_ON_LOAN",
|
|
183
|
+
trigger="self_checkout",
|
|
184
|
+
transition=ILSToItemOnLoan,
|
|
185
|
+
permission_factory=authenticated_user_permission,
|
|
186
|
+
),
|
|
175
187
|
dict(
|
|
176
188
|
dest="CANCELLED",
|
|
177
189
|
trigger="cancel",
|
|
@@ -12,9 +12,11 @@ from invenio_app_ils.records.loaders import ils_marshmallow_loader
|
|
|
12
12
|
from .schemas.json.bulk_extend import BulkExtendLoansSchemaV1
|
|
13
13
|
from .schemas.json.loan_checkout import LoanCheckoutSchemaV1
|
|
14
14
|
from .schemas.json.loan_request import LoanRequestSchemaV1
|
|
15
|
+
from .schemas.json.loan_self_checkout import LoanSelfCheckoutSchemaV1
|
|
15
16
|
from .schemas.json.loan_update_dates import LoanUpdateDatesSchemaV1
|
|
16
17
|
|
|
17
18
|
loan_request_loader = ils_marshmallow_loader(LoanRequestSchemaV1)
|
|
18
19
|
loan_checkout_loader = ils_marshmallow_loader(LoanCheckoutSchemaV1)
|
|
20
|
+
loan_self_checkout_loader = ils_marshmallow_loader(LoanSelfCheckoutSchemaV1)
|
|
19
21
|
loan_update_dates_loader = ils_marshmallow_loader(LoanUpdateDatesSchemaV1)
|
|
20
22
|
loans_bulk_update_loader = ils_marshmallow_loader(BulkExtendLoansSchemaV1)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2019 CERN.
|
|
4
|
+
#
|
|
5
|
+
# invenio-app-ils 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
|
+
"""Invenio App ILS circulation Loan Checkout loader JSON schema."""
|
|
9
|
+
|
|
10
|
+
from invenio_circulation.records.loaders.schemas.json import LoanItemPIDSchemaV1
|
|
11
|
+
from marshmallow import fields
|
|
12
|
+
|
|
13
|
+
from .base import LoanBaseSchemaV1
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class LoanSelfCheckoutSchemaV1(LoanBaseSchemaV1):
|
|
17
|
+
"""Loan self-checkout schema."""
|
|
18
|
+
|
|
19
|
+
item_pid = fields.Nested(LoanItemPIDSchemaV1, required=True)
|
invenio_app_ils/circulation/templates/invenio_app_ils_circulation/notifications/self_checkout.html
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{% block title %}
|
|
2
|
+
InvenioILS: loan started for "{{ document.title|safe }}"
|
|
3
|
+
{% endblock %}
|
|
4
|
+
|
|
5
|
+
{% block body_plain %}
|
|
6
|
+
Dear {{ patron.name }},
|
|
7
|
+
|
|
8
|
+
your self-checkout loan for "{{ document.full_title }}" <{{ spa_routes.HOST }}{{ spa_routes.PATHS['literature']|format(pid=document.pid) }}> has started.
|
|
9
|
+
|
|
10
|
+
The due date is {{ loan.end_date }}.
|
|
11
|
+
{% endblock %}
|
|
12
|
+
|
|
13
|
+
{% block body_html %}
|
|
14
|
+
Dear {{ patron.name }}, <br/><br/>
|
|
15
|
+
|
|
16
|
+
your self-checkout loan for <a href="{{ spa_routes.HOST }}{{ spa_routes.PATHS['literature']|format(pid=document.pid) }}">"{{ document.full_title }}"</a> has <b>started</b>. <br/><br/>
|
|
17
|
+
|
|
18
|
+
<b>The due date is {{ loan.end_date }}</b>.<br/>
|
|
19
|
+
{% endblock %}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
"""Invenio App ILS Circulation views."""
|
|
9
9
|
|
|
10
|
-
from flask import Blueprint, abort
|
|
10
|
+
from flask import Blueprint, abort, request
|
|
11
11
|
from flask_login import current_user
|
|
12
12
|
from invenio_circulation.links import loan_links_factory
|
|
13
13
|
from invenio_circulation.pidstore.pids import CIRCULATION_LOAN_PID_TYPE
|
|
@@ -18,15 +18,35 @@ from invenio_rest import ContentNegotiatedMethodView
|
|
|
18
18
|
from invenio_app_ils.circulation.loaders import (
|
|
19
19
|
loan_checkout_loader,
|
|
20
20
|
loan_request_loader,
|
|
21
|
+
loan_self_checkout_loader,
|
|
21
22
|
loan_update_dates_loader,
|
|
22
23
|
loans_bulk_update_loader,
|
|
23
24
|
)
|
|
24
25
|
from invenio_app_ils.circulation.utils import circulation_overdue_loan_days
|
|
25
|
-
from invenio_app_ils.errors import
|
|
26
|
+
from invenio_app_ils.errors import (
|
|
27
|
+
DocumentOverbookedError,
|
|
28
|
+
ItemCannotCirculateError,
|
|
29
|
+
ItemHasActiveLoanError,
|
|
30
|
+
ItemNotFoundError,
|
|
31
|
+
LoanSelfCheckoutDocumentOverbooked,
|
|
32
|
+
LoanSelfCheckoutItemActiveLoan,
|
|
33
|
+
LoanSelfCheckoutItemInvalidStatus,
|
|
34
|
+
LoanSelfCheckoutItemNotFound,
|
|
35
|
+
MissingRequiredParameterError,
|
|
36
|
+
OverdueLoansNotificationError,
|
|
37
|
+
)
|
|
38
|
+
from invenio_app_ils.items.api import ITEM_PID_TYPE
|
|
26
39
|
from invenio_app_ils.permissions import need_permissions
|
|
27
40
|
|
|
28
41
|
from ..patrons.api import patron_exists
|
|
29
|
-
from .api import
|
|
42
|
+
from .api import (
|
|
43
|
+
bulk_extend_loans,
|
|
44
|
+
checkout_loan,
|
|
45
|
+
request_loan,
|
|
46
|
+
self_checkout,
|
|
47
|
+
self_checkout_get_item_by_barcode,
|
|
48
|
+
update_dates_loan,
|
|
49
|
+
)
|
|
30
50
|
from .notifications.api import (
|
|
31
51
|
send_bulk_extend_notification,
|
|
32
52
|
send_loan_overdue_reminder_notification,
|
|
@@ -43,73 +63,97 @@ def create_circulation_blueprint(app):
|
|
|
43
63
|
url_prefix="",
|
|
44
64
|
)
|
|
45
65
|
|
|
46
|
-
|
|
47
|
-
options = endpoints.get(CIRCULATION_LOAN_PID_TYPE, {})
|
|
66
|
+
options = app.config["RECORDS_REST_ENDPOINTS"][CIRCULATION_LOAN_PID_TYPE]
|
|
48
67
|
default_media_type = options.get("default_media_type", "")
|
|
49
68
|
rec_serializers = options.get("record_serializers", {})
|
|
50
69
|
serializers = {
|
|
51
70
|
mime: obj_or_import_string(func) for mime, func in rec_serializers.items()
|
|
52
71
|
}
|
|
53
72
|
|
|
54
|
-
|
|
55
|
-
|
|
73
|
+
# /request
|
|
56
74
|
loan_request = LoanRequestResource.as_view(
|
|
57
75
|
LoanRequestResource.view_name,
|
|
58
76
|
serializers=serializers,
|
|
59
77
|
default_media_type=default_media_type,
|
|
60
78
|
ctx=dict(links_factory=loan_links_factory, loader=loan_request_loader),
|
|
61
79
|
)
|
|
62
|
-
|
|
63
80
|
blueprint.add_url_rule(
|
|
64
81
|
"/circulation/loans/request", view_func=loan_request, methods=["POST"]
|
|
65
82
|
)
|
|
66
83
|
|
|
84
|
+
# /checkout
|
|
67
85
|
loan_checkout = LoanCheckoutResource.as_view(
|
|
68
86
|
LoanCheckoutResource.view_name,
|
|
69
87
|
serializers=serializers,
|
|
70
88
|
default_media_type=default_media_type,
|
|
71
89
|
ctx=dict(links_factory=loan_links_factory, loader=loan_checkout_loader),
|
|
72
90
|
)
|
|
73
|
-
|
|
74
91
|
blueprint.add_url_rule(
|
|
75
92
|
"/circulation/loans/checkout",
|
|
76
93
|
view_func=loan_checkout,
|
|
77
94
|
methods=["POST"],
|
|
78
95
|
)
|
|
79
96
|
|
|
97
|
+
# /self-checkout
|
|
98
|
+
item_rec_serializers = app.config["RECORDS_REST_ENDPOINTS"][ITEM_PID_TYPE].get(
|
|
99
|
+
"record_serializers", {}
|
|
100
|
+
)
|
|
101
|
+
item_serializers = {
|
|
102
|
+
mime: obj_or_import_string(func) for mime, func in item_rec_serializers.items()
|
|
103
|
+
}
|
|
104
|
+
loan_self_checkout_item = LoanSelfCheckoutResource.as_view(
|
|
105
|
+
LoanSelfCheckoutResource.view_name,
|
|
106
|
+
serializers={}, # required, even if `method_serializers` will override it
|
|
107
|
+
method_serializers={
|
|
108
|
+
"GET": item_serializers,
|
|
109
|
+
"POST": serializers, # default loan serializers
|
|
110
|
+
},
|
|
111
|
+
default_media_type=default_media_type,
|
|
112
|
+
ctx=dict(
|
|
113
|
+
loan_links_factory=loan_links_factory, loader=loan_self_checkout_loader
|
|
114
|
+
),
|
|
115
|
+
)
|
|
116
|
+
blueprint.add_url_rule(
|
|
117
|
+
"/circulation/loans/self-checkout",
|
|
118
|
+
view_func=loan_self_checkout_item,
|
|
119
|
+
methods=["GET", "POST"],
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# /notification-overdue
|
|
80
123
|
loan_notification_overdue = LoanNotificationResource.as_view(
|
|
81
124
|
LoanNotificationResource.view_name.format(CIRCULATION_LOAN_PID_TYPE),
|
|
82
125
|
serializers=serializers,
|
|
83
126
|
default_media_type=default_media_type,
|
|
84
127
|
ctx=dict(links_factory=loan_links_factory),
|
|
85
128
|
)
|
|
86
|
-
|
|
87
129
|
blueprint.add_url_rule(
|
|
88
130
|
"{0}/notification-overdue".format(options["item_route"]),
|
|
89
131
|
view_func=loan_notification_overdue,
|
|
90
132
|
methods=["POST"],
|
|
91
133
|
)
|
|
92
134
|
|
|
135
|
+
# /bulk-extend
|
|
136
|
+
bulk_loan_extension_serializers = {"application/json": bulk_extend_v1_response}
|
|
137
|
+
|
|
93
138
|
bulk_loan_extension = BulkLoanExtensionResource.as_view(
|
|
94
139
|
BulkLoanExtensionResource.view_name,
|
|
95
140
|
serializers=bulk_loan_extension_serializers,
|
|
96
141
|
default_media_type=default_media_type,
|
|
97
142
|
ctx=dict(loader=loans_bulk_update_loader),
|
|
98
143
|
)
|
|
99
|
-
|
|
100
144
|
blueprint.add_url_rule(
|
|
101
145
|
"/circulation/bulk-extend",
|
|
102
146
|
view_func=bulk_loan_extension,
|
|
103
147
|
methods=["POST"],
|
|
104
148
|
)
|
|
105
149
|
|
|
150
|
+
# /update-dates
|
|
106
151
|
loan_update = LoanUpdateDatesResource.as_view(
|
|
107
152
|
LoanUpdateDatesResource.view_name.format(CIRCULATION_LOAN_PID_TYPE),
|
|
108
153
|
serializers=serializers,
|
|
109
154
|
default_media_type=default_media_type,
|
|
110
155
|
ctx=dict(links_factory=loan_links_factory, loader=loan_update_dates_loader),
|
|
111
156
|
)
|
|
112
|
-
|
|
113
157
|
blueprint.add_url_rule(
|
|
114
158
|
"{0}/update-dates".format(options["item_route"]),
|
|
115
159
|
view_func=loan_update,
|
|
@@ -157,6 +201,50 @@ class LoanCheckoutResource(IlsCirculationResource):
|
|
|
157
201
|
return self.make_response(pid, loan, 202, links_factory=self.links_factory)
|
|
158
202
|
|
|
159
203
|
|
|
204
|
+
class LoanSelfCheckoutResource(IlsCirculationResource):
|
|
205
|
+
"""Loan self-checkout action resource."""
|
|
206
|
+
|
|
207
|
+
view_name = "loan_self_checkout"
|
|
208
|
+
|
|
209
|
+
@need_permissions("circulation-loan-self-checkout")
|
|
210
|
+
def get(self, **kwargs):
|
|
211
|
+
"""Loan self-checkout GET method to retrieve items."""
|
|
212
|
+
try:
|
|
213
|
+
barcode = request.args["barcode"].upper()
|
|
214
|
+
assert barcode
|
|
215
|
+
except (KeyError, AssertionError):
|
|
216
|
+
msg = "Parameter `barcode` is missing"
|
|
217
|
+
raise MissingRequiredParameterError(description=msg)
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
pid, item = self_checkout_get_item_by_barcode(barcode)
|
|
221
|
+
except ItemCannotCirculateError:
|
|
222
|
+
raise LoanSelfCheckoutItemInvalidStatus()
|
|
223
|
+
except ItemHasActiveLoanError:
|
|
224
|
+
raise LoanSelfCheckoutItemActiveLoan()
|
|
225
|
+
except DocumentOverbookedError:
|
|
226
|
+
raise LoanSelfCheckoutDocumentOverbooked()
|
|
227
|
+
except ItemNotFoundError:
|
|
228
|
+
raise LoanSelfCheckoutItemNotFound()
|
|
229
|
+
|
|
230
|
+
return self.make_response(pid, item, 200)
|
|
231
|
+
|
|
232
|
+
@need_permissions("circulation-loan-self-checkout")
|
|
233
|
+
def post(self, **kwargs):
|
|
234
|
+
"""Loan self-checkout POST method to perform the checkout."""
|
|
235
|
+
data = self.loader()
|
|
236
|
+
try:
|
|
237
|
+
pid, loan = self_checkout(**data)
|
|
238
|
+
except ItemCannotCirculateError:
|
|
239
|
+
raise LoanSelfCheckoutItemInvalidStatus()
|
|
240
|
+
except ItemHasActiveLoanError:
|
|
241
|
+
raise LoanSelfCheckoutItemActiveLoan()
|
|
242
|
+
except DocumentOverbookedError:
|
|
243
|
+
raise LoanSelfCheckoutDocumentOverbooked()
|
|
244
|
+
|
|
245
|
+
return self.make_response(pid, loan, 202, links_factory=self.loan_links_factory)
|
|
246
|
+
|
|
247
|
+
|
|
160
248
|
class BulkLoanExtensionResource(IlsCirculationResource):
|
|
161
249
|
"""Bulk loan extension resource."""
|
|
162
250
|
|