def test_overdue_loans(client, librarian_martigny_no_email, patron_martigny_no_email, loc_public_martigny, item_type_standard_martigny, item_lib_martigny, circ_policy_short_martigny): """Test overdue loans.""" login_user_via_session(client, librarian_martigny_no_email.user) item = item_lib_martigny item_pid = item.pid patron_pid = patron_martigny_no_email.pid # checkout res, data = postdata( client, 'api_item.checkout', dict( item_pid=item_pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny_no_email.pid, ) ) assert res.status_code == 200, "It probably failed while \ test_due_soon_loans fail" loan_pid = data.get('action_applied')[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) end_date = datetime.now(timezone.utc) - timedelta(days=7) loan['end_date'] = end_date.isoformat() loan.update( loan, dbcommit=True, reindex=True ) overdue_loans = get_overdue_loans() assert overdue_loans[0].get('pid') == loan_pid assert number_of_reminders_sent(loan) == 0 loan.create_notification(notification_type='overdue') flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert number_of_reminders_sent(loan) == 1 # checkin the item to put it back to it's original state res, _ = postdata( client, 'api_item.checkin', dict( item_pid=item_pid, pid=loan_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny_no_email.pid, ) ) assert res.status_code == 200
def test_create_over_and_due_soon_notifications_task( client, librarian_martigny_no_email, patron_martigny_no_email, item_lib_martigny, circ_policy_short_martigny, loc_public_martigny, lib_martigny): """Test overdue and due_soon loans.""" login_user_via_session(client, librarian_martigny_no_email.user) item = item_lib_martigny item_pid = item.pid patron_pid = patron_martigny_no_email.pid # checkout res = client.post( url_for('api_item.checkout'), data=json.dumps(dict(item_pid=item_pid, patron_pid=patron_pid)), content_type='application/json', ) assert res.status_code == 200 data = get_json(res) loan_pid = data.get('action_applied')[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) # test due_soon notification end_date = datetime.now(timezone.utc) + timedelta(days=3) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) due_soon_loans = get_due_soon_loans() assert due_soon_loans[0].get('pid') == loan_pid create_over_and_due_soon_notifications() flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert loan.is_notified(notification_type='due_soon') # test overdue notification end_date = datetime.now(timezone.utc) - timedelta(days=7) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) overdue_loans = get_overdue_loans() assert overdue_loans[0].get('pid') == loan_pid create_over_and_due_soon_notifications() flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert loan.is_notified(notification_type='overdue') assert number_of_reminders_sent(loan) == 1 # checkin the item to put it back to it's original state res = client.post( url_for('api_item.checkin'), data=json.dumps(dict(item_pid=item_pid, pid=loan_pid)), content_type='application/json', ) assert res.status_code == 200
def test_loan_keep_and_to_anonymize( item_on_loan_martigny_patron_and_loan_on_loan, item2_on_loan_martigny_patron_and_loan_on_loan, librarian_martigny_no_email, loc_public_martigny): """Test anonymize and keep loan based on open transactions.""" item, patron, loan = item_on_loan_martigny_patron_and_loan_on_loan assert not loan.concluded(loan) assert not loan.can_anonymize(loan_data=loan) params = { 'transaction_location_pid': loc_public_martigny.pid, 'transaction_user_pid': librarian_martigny_no_email.pid } item, actions = item.checkin(**params) loan = Loan.get_record_by_pid(loan.pid) # item checkedin and has no open events assert loan.concluded(loan) assert not loan.can_anonymize(loan_data=loan) patron['patron']['keep_history'] = False patron.update(patron, dbcommit=True, reindex=True) # when the patron asks to anonymise history the can_anonymize is true loan = Loan.get_record_by_pid(loan.pid) assert loan.concluded(loan) assert loan.can_anonymize(loan_data=loan) loan = loan.update(loan, dbcommit=True, reindex=True) # test loans with fees item, patron, loan = item2_on_loan_martigny_patron_and_loan_on_loan assert not loan.concluded(loan) assert not loan.can_anonymize(loan_data=loan) end_date = datetime.now(timezone.utc) - timedelta(days=7) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) create_over_and_due_soon_notifications() flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) overdue_loans = list(get_overdue_loans()) params = { 'transaction_location_pid': loc_public_martigny.pid, 'transaction_user_pid': librarian_martigny_no_email.pid } item, actions = item.checkin(**params) loan = Loan.get_record_by_pid(loan.pid) assert not loan.concluded(loan) assert not loan.can_anonymize(loan_data=loan)
def test_create_fee(client, librarian_martigny_no_email, librarian_sion_no_email, patron_martigny_no_email, loc_public_martigny, item_type_standard_martigny, item_lib_martigny, json_header, circ_policy_short_martigny): """Test overdue loans.""" login_user_via_session(client, librarian_martigny_no_email.user) # checkout res, data = postdata( client, 'api_item.checkout', dict(item_pid=item_lib_martigny.pid, patron_pid=patron_martigny_no_email.pid)) assert res.status_code == 200 loan_pid = data.get('action_applied')[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) end_date = datetime.now(timezone.utc) - timedelta(days=7) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) overdue_loans = get_overdue_loans() assert overdue_loans[0].get('pid') == loan_pid notification = loan.create_notification(notification_type='overdue') flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) fee = list(notification.fees)[0] assert fee.get('amount') == 2 assert fee.get('currency') == 'CHF' fee_url = url_for('invenio_records_rest.fee_item', pid_value=fee.pid) res = client.get(fee_url) assert res.status_code == 200 login_user_via_session(client, librarian_sion_no_email.user) res = client.get(fee_url) assert res.status_code == 403 login_user_via_session(client, librarian_martigny_no_email.user) # checkin res, _ = postdata(client, 'api_item.checkin', dict(item_pid=item_lib_martigny.pid, pid=loan_pid)) assert res.status_code == 200
def test_create_fee_euro(client, librarian_martigny_no_email, item_lib_martigny, patron_martigny_no_email, json_header, circulation_policies): """ Test overdue loans with if we change the organisation default currency.""" login_user_via_session(client, librarian_martigny_no_email.user) # create a checkout res, data = postdata( client, 'api_item.checkout', { 'item_pid': item_lib_martigny.pid, 'patron_pid': patron_martigny_no_email.pid }, ) assert res.status_code == 200 # load the created loan and place it in overdue loan_pid = data.get('action_applied')[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) end_date = datetime.now(timezone.utc) - timedelta(days=7) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) overdue_loans = get_overdue_loans() assert overdue_loans[0].get('pid') == loan_pid # ensure that 'default_currency' of the linked organisation in 'EUR' org = Organisation.get_record_by_pid(loan.organisation_pid) org['default_currency'] = 'EUR' org.update(org, dbcommit=True, reindex=True) org = Organisation.get_record_by_pid(loan.organisation_pid) assert org.get('default_currency') == 'EUR' # create notification and check the created fee is in euro notification = loan.create_notification(notification_type='overdue') flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) fee = list(notification.fees)[0] assert fee.get('currency') == org.get('default_currency')
def test_overdue_limit(client, app, librarian_martigny, lib_martigny, item_lib_martigny, item2_lib_martigny, patron_type_children_martigny, item3_lib_martigny, item_lib_martigny_data, item2_lib_martigny_data, item3_lib_martigny_data, loc_public_martigny, patron_martigny, circ_policy_short_martigny): """Test overdue limit.""" item = item_lib_martigny item_pid = item.pid patron_pid = patron_martigny.pid # [0] prepare overdue transaction login_user_via_session(client, librarian_martigny.user) # checkout res, data = postdata( client, 'api_item.checkout', dict( item_pid=item_pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, )) assert res.status_code == 200 loan_pid = data.get('action_applied')[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) assert not loan.is_loan_overdue() end_date = datetime.now(timezone.utc) - timedelta(days=7) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) overdue_loans = list(get_overdue_loans(patron_pid=patron_pid)) assert loan.is_loan_overdue() assert loan.end_date == end_date.isoformat() assert overdue_loans[0].get('pid') == loan_pid assert number_of_reminders_sent(loan) == 0 loan.create_notification( notification_type=Notification.OVERDUE_NOTIFICATION_TYPE) flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert number_of_reminders_sent(loan) == 1 # [1] test overdue items limit # Update the patron_type to set a overdue_items_limit rule patron_type = patron_type_children_martigny patron_type \ .setdefault('limits', {}) \ .setdefault('overdue_items_limits', {}) \ .setdefault('default_value', 1) patron_type.update(patron_type, dbcommit=True, reindex=True) patron_type = PatronType.get_record_by_pid(patron_type.pid) assert patron_type.get('limits', {}).get('overdue_items_limits', {})\ .get('default_value') == 1 # [1.1] test overdue items limit when we try to checkout a second item res, data = postdata( client, 'api_item.checkout', dict( item_pid=item2_lib_martigny.pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, )) assert res.status_code == 403 assert 'Checkout denied' in data['message'] # [1.2] test overdue items limit when we try to request another item res, data = postdata( client, 'api_item.librarian_request', dict(item_pid=item2_lib_martigny.pid, patron_pid=patron_pid, pickup_location_pid=loc_public_martigny.pid, transaction_library_pid=lib_martigny.pid, transaction_user_pid=librarian_martigny.pid)) assert res.status_code == 403 assert 'maximal number of overdue items is reached' in data['message'] # [1.3] test overdue items limit when we try to extend loan res, _ = postdata( client, 'api_item.extend_loan', dict(item_pid=item_pid, transaction_user_pid=librarian_martigny.pid, transaction_location_pid=loc_public_martigny.pid)) assert res.status_code == 403 assert 'maximal number of overdue items is reached' in data['message'] # reset the patron_type with default value del patron_type['limits'] # [2] test fee amount limit # Update the patron_type to set a fee_amount_limit rule patron_type \ .setdefault('limits', {}) \ .setdefault('fee_amount_limits', {}) \ .setdefault('default_value', 0.5) patron_type.update(patron_type, dbcommit=True, reindex=True) patron_type = PatronType.get_record_by_pid(patron_type.pid) assert patron_type.get('limits', {}).get('fee_amount_limits', {}) \ .get('default_value') == 0.5 # [2.1] test fee amount limit when we try to checkout a second item res, data = postdata( client, 'api_item.checkout', dict( item_pid=item2_lib_martigny.pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, )) assert res.status_code == 403 assert 'maximal overdue fee amount is reached' in data['message'] # [2.2] test fee amount limit when we try to request another item res, data = postdata( client, 'api_item.librarian_request', dict(item_pid=item2_lib_martigny.pid, patron_pid=patron_pid, pickup_location_pid=loc_public_martigny.pid, transaction_library_pid=lib_martigny.pid, transaction_user_pid=librarian_martigny.pid)) assert res.status_code == 403 assert 'maximal overdue fee amount is reached' in data['message'] # [2.3] test fee amount limit when we try to extend loan res, _ = postdata( client, 'api_item.extend_loan', dict(item_pid=item_pid, transaction_user_pid=librarian_martigny.pid, transaction_location_pid=loc_public_martigny.pid)) assert res.status_code == 403 assert 'maximal overdue fee amount is reached' in data['message'] # reset the patron_type with default value del patron_type['limits'] patron_type.update(patron_type, dbcommit=True, reindex=True) patron_type = PatronType.get_record_by_pid(patron_type.pid) assert patron_type.get('limits') is None # # checkin the item to put it back to it's original state res, _ = postdata( client, 'api_item.checkin', dict( item_pid=item_pid, pid=loan_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, )) assert res.status_code == 200
def test_notifications_task( client, librarian_martigny, patron_martigny, item_lib_martigny, circ_policy_short_martigny, loc_public_martigny, lib_martigny): """Test overdue and due_soon loans.""" login_user_via_session(client, librarian_martigny.user) item = item_lib_martigny item_pid = item.pid patron_pid = patron_martigny.pid # First we need to create a checkout res, data = postdata( client, 'api_item.checkout', dict( item_pid=item_pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, ) ) assert res.status_code == 200 loan_pid = data.get('action_applied')[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) # test due_soon notification # update the loan end_date to reflect the due_soon date. So when we run # the task to create notification this loan should be considerate as # due_soon and a notification should be created. end_date = datetime.now(timezone.utc) + timedelta(days=5) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) flush_index(LoansSearch.Meta.index) due_soon_loans = list(get_due_soon_loans()) assert due_soon_loans[0].get('pid') == loan_pid create_notifications(types=[ NotificationType.DUE_SOON, NotificationType.OVERDUE ]) flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert loan.is_notified(NotificationType.DUE_SOON) notif = get_notification(loan, NotificationType.DUE_SOON) notif_date = ciso8601.parse_datetime(notif.get('creation_date')) assert notif_date.date() == datetime.today().date() # -- test overdue notification -- # For this test, we simulate an overdue on Friday and the library is closed # during the weekend. No notification should be generated. # Friday end_date = datetime(year=2021, month=1, day=22, tzinfo=timezone.utc) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) # Process the notification during the weekend (Saturday) process_date = datetime(year=2021, month=1, day=23, tzinfo=timezone.utc) overdue_loans = list(get_overdue_loans(tstamp=process_date)) assert overdue_loans[0].get('pid') == loan_pid create_notifications(types=[ NotificationType.OVERDUE ], tstamp=process_date) flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) # Should not be created assert not loan.is_notified(NotificationType.OVERDUE, 1) # Should not be sent assert number_of_notifications_sent( loan, notification_type=NotificationType.OVERDUE) == 0 # For this test, we will update the loan to simulate an overdue of 12 # days. With this delay, regarding the cipo configuration, only the first # overdue reminder should be sent. # NOTE : the cipo define the first overdue reminder after 5 days. But we # use an overdue of 12 days because the overdue is based on # loan->item->library open days. Using 12 (5 days + 1 week) we # ensure than the overdue notification will be sent. loan_lib = Library.get_record_by_pid(loan.library_pid) add_days = 12 open_days = [] while len(open_days) < 12: end_date = datetime.now(timezone.utc) - timedelta(days=add_days) open_days = loan_lib.get_open_days(end_date) add_days += 1 loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) overdue_loans = list(get_overdue_loans()) assert overdue_loans[0].get('pid') == loan_pid create_notifications(types=[ NotificationType.DUE_SOON, NotificationType.OVERDUE ]) flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert loan.is_notified(NotificationType.OVERDUE, 0) assert number_of_notifications_sent( loan, notification_type=NotificationType.OVERDUE) == 1 # test overdue notification#2 # Now simulate than the previous call crashed. So call the task with a # fixed date. In our test, no new notifications should be sent create_notifications(types=[ NotificationType.DUE_SOON, NotificationType.OVERDUE ], tstamp=datetime.now(timezone.utc)) assert number_of_notifications_sent( loan, notification_type=NotificationType.OVERDUE) == 1 # test overdue notification#3 # For this test, we will update the loan to simulate an overdue of 40 # days. With this delay, regarding the cipo configuration, the second # (and last) overdue reminder should be sent. end_date = datetime.now(timezone.utc) - timedelta(days=40) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) overdue_loans = list(get_overdue_loans()) assert overdue_loans[0].get('pid') == loan_pid create_notifications(types=[ NotificationType.DUE_SOON, NotificationType.OVERDUE ]) flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert loan.is_notified(NotificationType.OVERDUE, 1) assert number_of_notifications_sent( loan, notification_type=NotificationType.OVERDUE) == 2 # checkin the item to put it back to it's original state res, _ = postdata( client, 'api_item.checkin', dict( item_pid=item_pid, pid=loan_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid ) ) assert res.status_code == 200
def test_overdue_limit( client, app, librarian_martigny, lib_martigny, item_lib_martigny, item2_lib_martigny, patron_type_children_martigny, item3_lib_martigny, item_lib_martigny_data, item2_lib_martigny_data, item3_lib_martigny_data, loc_public_martigny, patron_martigny, circ_policy_short_martigny, lib_saxon, loc_public_saxon): """Test overdue limit.""" item = item_lib_martigny item_pid = item.pid patron_pid = patron_martigny.pid date_format = '%Y/%m/%dT%H:%M:%S.000Z' today = datetime.utcnow() eod = today.replace(hour=23, minute=59, second=0, microsecond=0, tzinfo=lib_martigny.get_timezone()) # STEP 0 :: Prepare data for test # * Update the patron_type to set a overdue_items_limit rule. # We define than only 1 overdue items are allowed. Trying a second # checkout is disallowed if patron has an overdue item patron_type = patron_type_children_martigny patron_type \ .setdefault('limits', {}) \ .setdefault('overdue_items_limits', {}) \ .setdefault('default_value', 1) patron_type.update(patron_type, dbcommit=True, reindex=True) patron_type = PatronType.get_record_by_pid(patron_type.pid) assert patron_type\ .get('limits', {})\ .get('overdue_items_limits', {}) \ .get('default_value') == 1 # STEP 1 :: Create an checkout with a end_date at the current date # * Create a checkout and set end_date to a fixed_date equals to # current tested date. The loan should not be considered as overdue # and a second checkout should be possible login_user_via_session(client, librarian_martigny.user) res, data = postdata( client, 'api_item.checkout', dict( item_pid=item_pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, end_date=eod.strftime(date_format) ) ) assert res.status_code == 200 loan_pid = data.get('action_applied')[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) assert not loan.is_loan_overdue() tmp_item_data = deepcopy(item_lib_martigny_data) del tmp_item_data['pid'] tmp_item_data['library']['$ref'] = get_ref_for_pid('lib', lib_saxon.pid) tmp_item_data['location']['$ref'] = \ get_ref_for_pid('loc', loc_public_saxon.pid) tmp_item = Item.create(tmp_item_data, dbcommit=True, reindex=True) res, data = postdata( client, 'api_item.checkout', dict( item_pid=tmp_item.pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid ) ) assert res.status_code == 200 res, _ = postdata( client, 'api_item.checkin', dict( item_pid=tmp_item.pid, pid=data.get('action_applied')[LoanAction.CHECKOUT].get('pid'), transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, ) ) assert res.status_code == 200 # STEP 2 :: Set the loan as overdue and test a new checkout # Now there is one loan in overdue, then the limit is reached and a new # checkout shouldn't be possible end_date = eod - timedelta(days=7) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) overdue_loans = list(get_overdue_loans(patron_pid=patron_pid)) assert loan.is_loan_overdue() assert loan.end_date == end_date.isoformat() assert overdue_loans[0].get('pid') == loan_pid assert not get_notification(loan, NotificationType.OVERDUE) notification = loan.create_notification( _type=NotificationType.OVERDUE).pop() Dispatcher.dispatch_notifications([notification.get('pid')]) flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert get_notification(loan, NotificationType.OVERDUE) # Try a second checkout - limit should be reached res, data = postdata( client, 'api_item.checkout', dict( item_pid=item2_lib_martigny.pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, ) ) assert res.status_code == 403 assert 'Checkout denied' in data['message'] # Try a request - limit should be reached res, data = postdata( client, 'api_item.librarian_request', dict( item_pid=item2_lib_martigny.pid, patron_pid=patron_pid, pickup_location_pid=loc_public_martigny.pid, transaction_library_pid=lib_martigny.pid, transaction_user_pid=librarian_martigny.pid ) ) assert res.status_code == 403 assert 'maximal number of overdue items is reached' in data['message'] # Try to extend - limit should be reached res, _ = postdata( client, 'api_item.extend_loan', dict( item_pid=item_pid, transaction_user_pid=librarian_martigny.pid, transaction_location_pid=loc_public_martigny.pid ) ) assert res.status_code == 403 assert 'maximal number of overdue items is reached' in data['message'] # reset the patron_type with default value del patron_type['limits'] # [2] test fee amount limit # Update the patron_type to set a fee_amount_limit rule patron_type \ .setdefault('limits', {}) \ .setdefault('fee_amount_limits', {}) \ .setdefault('default_value', 0.5) patron_type.update(patron_type, dbcommit=True, reindex=True) patron_type = PatronType.get_record_by_pid(patron_type.pid) assert patron_type.get('limits', {}).get('fee_amount_limits', {}) \ .get('default_value') == 0.5 # [2.1] test fee amount limit when we try to checkout a second item res, data = postdata( client, 'api_item.checkout', dict( item_pid=item2_lib_martigny.pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, ) ) assert res.status_code == 403 assert 'maximal overdue fee amount is reached' in data['message'] # [2.2] test fee amount limit when we try to request another item res, data = postdata( client, 'api_item.librarian_request', dict( item_pid=item2_lib_martigny.pid, patron_pid=patron_pid, pickup_location_pid=loc_public_martigny.pid, transaction_library_pid=lib_martigny.pid, transaction_user_pid=librarian_martigny.pid ) ) assert res.status_code == 403 assert 'maximal overdue fee amount is reached' in data['message'] # [2.3] test fee amount limit when we try to extend loan res, _ = postdata( client, 'api_item.extend_loan', dict( item_pid=item_pid, transaction_user_pid=librarian_martigny.pid, transaction_location_pid=loc_public_martigny.pid ) ) assert res.status_code == 403 assert 'maximal overdue fee amount is reached' in data['message'] # reset the patron_type with default value del patron_type['limits'] patron_type.update(patron_type, dbcommit=True, reindex=True) patron_type = PatronType.get_record_by_pid(patron_type.pid) assert patron_type.get('limits') is None # # checkin the item to put it back to it's original state res, _ = postdata( client, 'api_item.checkin', dict( item_pid=item_pid, pid=loan_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, ) ) assert res.status_code == 200
def test_overdue_loans(client, librarian_martigny, patron_martigny, loc_public_martigny, item_type_standard_martigny, item_lib_martigny, item2_lib_martigny, patron_type_children_martigny, circ_policy_short_martigny, patron3_martigny_blocked): """Test overdue loans.""" login_user_via_session(client, librarian_martigny.user) item = item_lib_martigny item_pid = item.pid patron_pid = patron_martigny.pid # checkout res, data = postdata( client, 'api_item.checkout', dict( item_pid=item_pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, )) assert res.status_code == 200, "It probably failed while \ test_due_soon_loans fail" loan_pid = data.get('action_applied')[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) end_date = datetime.now(timezone.utc) - timedelta(days=7) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) overdue_loans = list(get_overdue_loans(patron_pid=patron_pid)) assert overdue_loans[0].get('pid') == loan_pid assert number_of_notifications_sent(loan) == 0 notification = loan.create_notification( _type=NotificationType.OVERDUE).pop() Dispatcher.dispatch_notifications([notification.get('pid')]) flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert number_of_notifications_sent(loan) == 1 # Try a checkout for a blocked user :: It should be blocked res, data = postdata( client, 'api_item.checkout', dict( item_pid=item2_lib_martigny.pid, patron_pid=patron3_martigny_blocked.pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, )) assert res.status_code == 403 assert 'This patron is currently blocked' in data['message'] # checkin the item to put it back to it's original state res, _ = postdata( client, 'api_item.checkin', dict( item_pid=item_pid, pid=loan_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, )) assert res.status_code == 200
def test_notifications_task(client, librarian_martigny, patron_martigny, item_lib_martigny, circ_policy_short_martigny, loc_public_martigny, lib_martigny): """Test overdue and due_soon loans.""" login_user_via_session(client, librarian_martigny.user) item = item_lib_martigny item_pid = item.pid patron_pid = patron_martigny.pid # First we need to create a checkout res, data = postdata( client, 'api_item.checkout', dict( item_pid=item_pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, )) assert res.status_code == 200 loan_pid = data.get('action_applied')[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) # test due_soon notification end_date = datetime.now(timezone.utc) + timedelta(days=3) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) due_soon_loans = get_due_soon_loans() assert due_soon_loans[0].get('pid') == loan_pid create_notifications(types=[ Notification.DUE_SOON_NOTIFICATION_TYPE, Notification.OVERDUE_NOTIFICATION_TYPE ]) flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert loan.is_notified(Notification.DUE_SOON_NOTIFICATION_TYPE) # test overdue notification # For this test, we will update the loan to simulate an overdue of 12 # days. With this delay, regarding the cipo configuration, only the first # overdue reminder should be sent. # NOTE : the cipo define the first overdue reminder after 5 days. But we # use an overdue of 12 days because the overdue is based on # loan->item->library open days. Using 12 (5 days + 1 week) we # ensure than the overdue notification will be sent. end_date = datetime.now(timezone.utc) - timedelta(days=12) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) overdue_loans = list(get_overdue_loans()) assert overdue_loans[0].get('pid') == loan_pid create_notifications(types=[ Notification.DUE_SOON_NOTIFICATION_TYPE, Notification.OVERDUE_NOTIFICATION_TYPE ], process=False) flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert loan.is_notified(Notification.OVERDUE_NOTIFICATION_TYPE, 0) assert number_of_reminders_sent( loan, notification_type=Notification.OVERDUE_NOTIFICATION_TYPE) == 1 # test overdue notification#2 # Now simulate than the previous call crashed. So call the task with a # fixed date. In our test, no new notifications should be sent create_notifications(types=[ Notification.DUE_SOON_NOTIFICATION_TYPE, Notification.OVERDUE_NOTIFICATION_TYPE ], tstamp=datetime.now(timezone.utc), process=False) assert number_of_reminders_sent( loan, notification_type=Notification.OVERDUE_NOTIFICATION_TYPE) == 1 # test overdue notification#3 # For this test, we will update the loan to simulate an overdue of 40 # days. With this delay, regarding the cipo configuration, the second # (and last) overdue reminder should be sent. end_date = datetime.now(timezone.utc) - timedelta(days=40) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) overdue_loans = list(get_overdue_loans()) assert overdue_loans[0].get('pid') == loan_pid create_notifications(types=[ Notification.DUE_SOON_NOTIFICATION_TYPE, Notification.OVERDUE_NOTIFICATION_TYPE ], process=False) flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert loan.is_notified(Notification.OVERDUE_NOTIFICATION_TYPE, 1) assert number_of_reminders_sent( loan, notification_type=Notification.OVERDUE_NOTIFICATION_TYPE) == 2 # checkin the item to put it back to it's original state res, _ = postdata( client, 'api_item.checkin', dict(item_pid=item_pid, pid=loan_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid)) assert res.status_code == 200