예제 #1
0
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
예제 #2
0
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
예제 #3
0
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)
예제 #4
0
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
예제 #5
0
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')
예제 #6
0
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
예제 #7
0
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
예제 #8
0
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
예제 #9
0
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
예제 #10
0
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