Skip to content

Commit 6f62ffc

Browse files
committed
sip2: implements selfcheck renewal
* Allows the patron to do a renewal on the selfcheck machine. * Adds CLI for selfcheck terminal creation. * Adds renew handler. Lauren-D <[email protected]>
1 parent b737a22 commit 6f62ffc

File tree

5 files changed

+154
-51
lines changed

5 files changed

+154
-51
lines changed

rero_ils/config.py

+1
Original file line numberDiff line numberDiff line change
@@ -2708,6 +2708,7 @@ def _(x):
27082708
circulation_handlers=dict(
27092709
checkout='rero_ils.modules.selfcheck.api:selfcheck_checkout',
27102710
checkin='rero_ils.modules.selfcheck.api:selfcheck_checkin',
2711+
renew='rero_ils.modules.selfcheck.api:selfcheck_renew',
27112712
)
27122713
)
27132714
)

rero_ils/modules/cli.py

+2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
from .monitoring import Monitoring
7676
from .operation_logs.cli import create_operation_logs, dump_operation_logs
7777
from .patrons.cli import import_users, users_validate
78+
from .selfcheck.cli import terminal_create
7879
from .tasks import process_bulk_queue
7980
from .utils import bulk_load_metadata, bulk_load_pids, bulk_load_pidstore, \
8081
bulk_save_metadata, bulk_save_pids, bulk_save_pidstore, \
@@ -132,6 +133,7 @@ def utils():
132133

133134

134135
utils.add_command(users_validate)
136+
utils.add_command(terminal_create)
135137

136138

137139
def queue_count():

rero_ils/modules/selfcheck/api.py

+72-3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
map_item_circulation_status, map_media_type
2929
from ..documents.api import Document
3030
from ..documents.utils import title_format_text_head
31+
from ..errors import NoCirculationAction
3132
from ..items.api import Item
3233
from ..items.models import ItemNoteTypes
3334
from ..libraries.api import Library
@@ -351,9 +352,8 @@ def selfcheck_checkout(transaction_user_pid, item_barcode, patron_barcode,
351352
item_pid=item.pid,
352353
selfcheck_terminal_id=str(terminal.id),
353354
)
354-
loan_pid = data[LoanAction.CHECKOUT].get('pid')
355-
loan = Loan.get_record_by_pid(loan_pid)
356-
if loan:
355+
if data[LoanAction.CHECKOUT]:
356+
loan = data[LoanAction.CHECKOUT]
357357
checkout['checkout'] = True
358358
checkout['due_date'] = loan.get_loan_end_date(
359359
time_format=None, language=language)
@@ -413,6 +413,7 @@ def selfcheck_checkin(transaction_user_pid, item_barcode, **kwargs):
413413
transaction_user_pid=staffer.pid,
414414
transaction_library_pid=terminal.library_pid,
415415
item_pid=item.pid,
416+
selfcheck_terminal_id=str(terminal.id),
416417
)
417418
if data[LoanAction.CHECKIN]:
418419
checkin['checkin'] = True
@@ -428,3 +429,71 @@ def selfcheck_checkin(transaction_user_pid, item_barcode, **kwargs):
428429
_('Error encountered: please contact a librarian'))
429430
raise SelfcheckCirculationError('self checkin failed', checkin)
430431
return checkin
432+
433+
434+
def selfcheck_renew(transaction_user_pid, item_barcode, **kwargs):
435+
"""SIP2 handler to perform renew.
436+
437+
Perform renew action received from the selfcheck.
438+
:param transaction_user_pid: identifier of the staff user.
439+
:param item_barcode: item identifier.
440+
:return: The SelfcheckRenew object.
441+
"""
442+
if check_sip2_module():
443+
from invenio_sip2.errors import SelfcheckCirculationError
444+
from invenio_sip2.models import SelfcheckFeeType, SelfcheckRenew
445+
446+
terminal = SelfcheckTerminal.find_terminal(
447+
name=kwargs.get('terminal'))
448+
item = Item.get_item_by_barcode(
449+
barcode=item_barcode,
450+
organisation_pid=terminal.organisation_pid
451+
)
452+
document = Document.get_record_by_pid(item.document_pid)
453+
454+
renew = SelfcheckRenew(
455+
title_id=title_format_text_head(document.get('title'))
456+
)
457+
with current_app.test_request_context() as ctx:
458+
language = kwargs.get('language', current_app.config
459+
.get('BABEL_DEFAULT_LANGUAGE'))
460+
ctx.babel_locale = language
461+
try:
462+
staffer = Patron.get_record_by_pid(transaction_user_pid)
463+
if staffer.is_librarian:
464+
# get renewal count
465+
renewal_count = item.get_extension_count()
466+
if renewal_count > 1:
467+
renew['renewal'] = True
468+
# do extend loan
469+
result, data = item.extend_loan(
470+
transaction_user_pid=staffer.pid,
471+
transaction_library_pid=terminal.library_pid,
472+
item_pid=item.pid,
473+
selfcheck_terminal_id=str(terminal.id),
474+
)
475+
if data[LoanAction.EXTEND]:
476+
loan = data[LoanAction.EXTEND]
477+
renew['success'] = True
478+
renew['due_date'] = loan.get_loan_end_date(
479+
time_format=None, language=language)
480+
transaction = PatronTransaction. \
481+
get_last_transaction_by_loan_pid(
482+
loan_pid=loan.pid,
483+
status='open')
484+
if transaction:
485+
# TODO: map transaction type
486+
renew['fee_type'] = SelfcheckFeeType.OVERDUE
487+
renew['fee_amount'] = transaction.total_amount
488+
renew['currency_type'] = transaction.currency
489+
# TODO: When is possible, try to return fields:
490+
# magnetic_media, resensitize
491+
492+
except NoCirculationAction:
493+
renew.get('screen_messages', []).append(
494+
_('No circulation action is possible'))
495+
except Exception:
496+
renew.get('screen_messages', []).append(
497+
_('Error encountered: please contact a librarian'))
498+
raise SelfcheckCirculationError('self renewal failed', renew)
499+
return renew

rero_ils/modules/selfcheck/cli.py

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# RERO ILS
4+
# Copyright (C) 2021 RERO
5+
# Copyright (C) 2021 UCLouvain
6+
#
7+
# This program is free software: you can redistribute it and/or modify
8+
# it under the terms of the GNU Affero General Public License as published by
9+
# the Free Software Foundation, version 3 of the License.
10+
#
11+
# This program is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU Affero General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU Affero General Public License
17+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
19+
"""Click command-line utilities."""
20+
21+
import click
22+
from flask.cli import with_appcontext
23+
from invenio_db import db
24+
from invenio_oauth2server.cli import process_scopes, process_user
25+
26+
from rero_ils.modules.locations.api import search_location_by_pid
27+
28+
from .models import SelfcheckTerminal
29+
30+
31+
@click.command('terminal_create')
32+
@click.option('-n', '--name', required=True)
33+
@click.option(
34+
'-u', '--user', required=True, callback=process_user,
35+
help='User ID or email.')
36+
@click.option(
37+
'-l', '--location-pid', required=True)
38+
@click.option(
39+
'-s', '--scope', 'scopes', multiple=True, callback=process_scopes)
40+
@click.option('-i', '--internal', is_flag=True)
41+
@click.option(
42+
'-t', '--access_token', 'access_token', required=False,
43+
help='personalized access_token.')
44+
@with_appcontext
45+
def terminal_create(name, user, location_pid, scopes, internal,
46+
access_token):
47+
"""Create a personal OAuth token."""
48+
from rero_ils.modules.cli import create_personal # avoid circular import
49+
50+
location = search_location_by_pid(location_pid)
51+
if location:
52+
token = create_personal(
53+
name, user.id, scopes=scopes, is_internal=internal,
54+
access_token=access_token)
55+
selfcheck_terminal = SelfcheckTerminal(
56+
name=name,
57+
access_token=token.access_token,
58+
organisation_pid=location.organisation['pid'],
59+
library_pid=location.library['pid'],
60+
location_pid=location_pid
61+
)
62+
db.session.add(selfcheck_terminal)
63+
db.session.commit()
64+
click.secho(f'login: {name}', fg='blue')
65+
click.secho(f'token: {token.access_token}', fg='blue')

tests/api/selfcheck/test_selfcheck.py

+14-48
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from rero_ils.modules.notifications.dispatcher import Dispatcher
3434
from rero_ils.modules.selfcheck.api import authorize_patron, enable_patron, \
3535
item_information, patron_information, selfcheck_checkin, \
36-
selfcheck_checkout, selfcheck_login, system_status, \
36+
selfcheck_checkout, selfcheck_login, selfcheck_renew, system_status, \
3737
validate_patron_account
3838
from rero_ils.modules.selfcheck.utils import check_sip2_module
3939

@@ -259,11 +259,11 @@ def test_item_information(client, librarian_martigny,
259259
assert res.status_code == 200
260260

261261

262-
def test_selfcheck_checkout(client, selfcheck_librarian_martigny,
263-
selfcheck_patron_martigny, loc_public_martigny,
264-
item_lib_martigny, librarian_martigny,
265-
librarian2_martigny, circulation_policies):
266-
"""Test selfcheck checkout."""
262+
def test_selfcheck_circulation(client, selfcheck_librarian_martigny, document,
263+
librarian_martigny, librarian2_martigny,
264+
loc_public_martigny, selfcheck_patron_martigny,
265+
item_lib_martigny, circulation_policies):
266+
"""Test selfcheck circulation operation."""
267267
patron_barcode = selfcheck_patron_martigny \
268268
.get('patron', {}).get('barcode')[0]
269269
item_barcode = item_lib_martigny.get('barcode')
@@ -278,49 +278,15 @@ def test_selfcheck_checkout(client, selfcheck_librarian_martigny,
278278
assert checkout.is_success
279279
assert checkout.due_date
280280

281-
# librarian checkin
282-
login_user_via_session(client, librarian2_martigny.user)
283-
res, _ = postdata(
284-
client,
285-
'api_item.checkin',
286-
dict(
287-
item_pid=item_lib_martigny.pid,
288-
transaction_user_pid=librarian2_martigny.pid,
289-
transaction_location_pid=loc_public_martigny.pid
290-
)
281+
# selfcheck renew
282+
renew = selfcheck_renew(
283+
transaction_user_pid=librarian_martigny.pid,
284+
item_barcode=item_barcode, patron_barcode=patron_barcode,
285+
terminal=selfcheck_librarian_martigny.name
291286
)
292-
assert res.status_code == 200
293-
294-
295-
def test_selfcheck_checkin(client, selfcheck_librarian_martigny,
296-
librarian_martigny, librarian2_martigny,
297-
loc_public_martigny, selfcheck_patron_martigny,
298-
item_lib_martigny, document, circulation_policies):
299-
"""Test selfcheck checkin."""
300-
patron_barcode = selfcheck_patron_martigny \
301-
.get('patron', {}).get('barcode')[0]
302-
item_barcode = item_lib_martigny.get('barcode')
303-
304-
# librarian checkout
305-
login_user_via_session(client, librarian2_martigny.user)
306-
res, data = postdata(client, 'api_item.checkout', dict(
307-
item_pid=item_lib_martigny.pid,
308-
patron_pid=selfcheck_patron_martigny.pid,
309-
transaction_location_pid=loc_public_martigny.pid,
310-
transaction_user_pid=librarian2_martigny.pid
311-
))
312-
assert res.status_code == 200
313-
314-
# test selfcheck checkin with invalid item barcode
315-
with pytest.raises(Exception):
316-
checkin = selfcheck_checkin(
317-
transaction_user_pid=librarian_martigny.pid,
318-
patron_barcode=patron_barcode,
319-
item_barcode='wrong_item_barcode',
320-
terminal=selfcheck_librarian_martigny.name
321-
)
322-
assert checkin
323-
assert not checkin.is_success
287+
assert renew
288+
assert renew.is_success
289+
assert renew.due_date
324290

325291
# selfcheck checkin
326292
checkin = selfcheck_checkin(

0 commit comments

Comments
 (0)