Пример #1
0
def test_cancel_pending_on_item_in_transit_to_house(
        client, item3_in_transit_martigny_patron_and_loan_to_house,
        loc_public_martigny, librarian_martigny_no_email,
        patron2_martigny_no_email):
    """Test cancel pending loan on an in_transit to_house item."""
    item, patron, loan = item3_in_transit_martigny_patron_and_loan_to_house
    # the following tests the circulation action CANCEL_REQUEST_5_2
    # an item in_transit to house with pending loans. when a
    # librarian wants to cancel the pending loan. action is permitted.
    # the loan will be cancelled. and in_transit loan remains in transit.
    params = {
        'patron_pid': patron2_martigny_no_email.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny_no_email.pid,
        'pickup_location_pid': loc_public_martigny.pid
    }
    item, requested_loan = item_record_to_a_specific_loan_state(
        item=item,
        loan_state=LoanState.PENDING,
        params=params,
        copy_item=False)
    assert requested_loan['state'] == LoanState.PENDING

    params = {
        'pid': requested_loan.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny_no_email.pid
    }
    item.cancel_item_request(**params)
    item = Item.get_record_by_pid(item.pid)
    loan = Loan.get_record_by_pid(loan.pid)
    requested_loan = Loan.get_record_by_pid(requested_loan.pid)
    assert item.status == ItemStatus.IN_TRANSIT
    assert loan['state'] == LoanState.ITEM_IN_TRANSIT_TO_HOUSE
    assert requested_loan['state'] == LoanState.CANCELLED
def test_validate_on_item_at_desk(
        item_at_desk_martigny_patron_and_loan_at_desk,
        loc_public_martigny, librarian_martigny,
        circulation_policies, patron2_martigny):
    """Test validate a request on an item at_desk."""
    # the following tests the circulation action VALIDATE_2
    # on at_desk item, the validation is not possible
    item, patron, loan = item_at_desk_martigny_patron_and_loan_at_desk
    params = {
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny.pid,
        'pid': loan.pid
    }
    with pytest.raises(NoValidTransitionAvailableError):
        item, actions = item.validate_request(**params)
    assert item.status == ItemStatus.AT_DESK
    loan = Loan.get_record_by_pid(loan.pid)
    assert loan['state'] == LoanState.ITEM_AT_DESK
    # will not be able to validate any requestes for this item
    params = {
        'patron_pid': patron2_martigny.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny.pid,
        'pickup_location_pid': loc_public_martigny.pid
    }
    item, requested_loan = item_record_to_a_specific_loan_state(
        item=item, loan_state=LoanState.PENDING, params=params,
        copy_item=False)
    params['pid'] = requested_loan.pid
    with pytest.raises(NoValidTransitionAvailableError):
        item, actions = item.validate_request(**params)
    requested_loan = Loan.get_record_by_pid(requested_loan.pid)
    assert requested_loan['state'] == LoanState.PENDING
    loan = Loan.get_record_by_pid(loan.pid)
    assert loan['state'] == LoanState.ITEM_AT_DESK
Пример #3
0
def test_cancel_pending_loan_on_item_in_transit_for_pickup_with_requests(
        client, item3_in_transit_martigny_patron_and_loan_for_pickup,
        loc_public_martigny, librarian_martigny, patron2_martigny):
    """Test cancel pending loan on an in_transit for pickup item."""
    item, patron, loan = item3_in_transit_martigny_patron_and_loan_for_pickup
    # the following tests the circulation action CANCEL_REQUEST_4_2
    # an item in_transit for pickup with other pending loans. when a
    # librarian wants to cancel the pending loan. action is permitted.
    # item remains in_transit

    params = {
        'patron_pid': patron2_martigny.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny.pid,
        'pickup_location_pid': loc_public_martigny.pid
    }
    item, requested_loan = item_record_to_a_specific_loan_state(
        item=item,
        loan_state=LoanState.PENDING,
        params=params,
        copy_item=False)
    assert requested_loan['state'] == LoanState.PENDING

    params = {
        'pid': requested_loan.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny.pid
    }
    item.cancel_item_request(**params)
    item = Item.get_record_by_pid(item.pid)
    loan = Loan.get_record_by_pid(loan.pid)
    requested_loan = Loan.get_record_by_pid(requested_loan.pid)
    assert item.status == ItemStatus.IN_TRANSIT
    assert loan['state'] == LoanState.ITEM_IN_TRANSIT_FOR_PICKUP
    assert requested_loan['state'] == LoanState.CANCELLED
Пример #4
0
def test_checkin_on_item_on_shelf_with_requests(
        item_on_shelf_martigny_patron_and_loan_pending,
        loc_public_martigny, librarian_martigny,
        patron2_martigny, loc_public_fully, lib_martigny):
    """Test checkin on an on_shelf item with requests."""
    item, patron, loan = item_on_shelf_martigny_patron_and_loan_pending
    # the following tests the circulation action CHECKIN_1_2_1
    # for an item on_shelf with pending loans, the pickup library of the first
    # pending loan equal to the transaction library, the first pending loan
    # is validated and item assigned the at_desk
    # validate_request circulation action will be performed.

    # create a second pending loan on same item
    params = {
        'patron_pid': patron2_martigny.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny.pid,
        'pickup_location_pid': loc_public_fully.pid
    }
    item, requested_loan = item_record_to_a_specific_loan_state(
        item=item, loan_state=LoanState.PENDING,
        params=params, copy_item=False)
    assert requested_loan['state'] == LoanState.PENDING

    params = {
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny.pid
    }
    item, actions = item.checkin(**params)
    assert item.status == ItemStatus.AT_DESK
    assert Loan.get_record_by_pid(loan.pid)['state'] == LoanState.ITEM_AT_DESK
    assert Loan.get_record_by_pid(requested_loan.pid)['state'] == \
        LoanState.PENDING
Пример #5
0
def test_checkin_on_item_on_loan_with_requests(
        item3_on_loan_martigny_patron_and_loan_on_loan, loc_public_martigny,
        librarian_martigny_no_email, patron2_martigny_no_email):
    """Test checkin on an on_loan item with requests at local library."""
    item, patron, loan = item3_on_loan_martigny_patron_and_loan_on_loan
    # the following tests the circulation action CHECKIN_3_2_1
    # for an item on_loan, with pending requests. when the pickup library of
    # the first pending request equal to the transaction library,
    # checkin the item and item becomes at_desk.
    # the on_loan is returned and validating the first pending loan request.
    params = {
        'patron_pid': patron2_martigny_no_email.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny_no_email.pid,
        'pickup_location_pid': loc_public_martigny.pid
    }

    item, requested_loan = item_record_to_a_specific_loan_state(
        item=item,
        loan_state=LoanState.PENDING,
        params=params,
        copy_item=False)

    item, actions = item.checkin(**params)
    item = Item.get_record_by_pid(item.pid)
    loan = Loan.get_record_by_pid(loan.pid)
    requested_loan = Loan.get_record_by_pid(requested_loan.pid)
    assert item.status == ItemStatus.AT_DESK
    assert loan['state'] == LoanState.ITEM_RETURNED
    assert requested_loan['state'] == LoanState.ITEM_AT_DESK
def test_cancel_request_on_item_at_desk_with_requests_externally(
        client, item3_at_desk_martigny_patron_and_loan_at_desk,
        loc_public_martigny, librarian_martigny_no_email,
        patron2_martigny_no_email, loc_public_fully):
    """Test cancel requests on an at_desk item with requests at externally."""
    item, patron, loan = item3_at_desk_martigny_patron_and_loan_at_desk
    # the following tests the circulation action CANCEL_REQUEST_2_1_2_1
    # an item at_desk with other pending loans.
    # pickup location of 1st pending loan != pickup location of current loan
    # cancel the current loan and item is: in_transit, automatic validation of
    # firt pending loan

    params = {
        'patron_pid': patron2_martigny_no_email.pid,
        'transaction_location_pid': loc_public_fully.pid,
        'transaction_user_pid': librarian_martigny_no_email.pid,
        'pickup_location_pid': loc_public_fully.pid
    }
    item, requested_loan = item_record_to_a_specific_loan_state(
        item=item, loan_state=LoanState.PENDING,
        params=params, copy_item=False)
    assert requested_loan['state'] == LoanState.PENDING

    params = {
        'pid': loan.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny_no_email.pid
    }
    item.cancel_item_request(**params)
    item = Item.get_record_by_pid(item.pid)
    loan = Loan.get_record_by_pid(loan.pid)
    requested_loan = Loan.get_record_by_pid(requested_loan.pid)
    assert item.status == ItemStatus.IN_TRANSIT
    assert loan['state'] == LoanState.CANCELLED
    assert requested_loan['state'] == LoanState.ITEM_IN_TRANSIT_FOR_PICKUP
Пример #7
0
def test_cancel_request_on_item_in_transit_to_house_with_requests(
        client, item2_in_transit_martigny_patron_and_loan_to_house,
        loc_public_martigny, librarian_martigny, patron2_martigny):
    """Test cancel request on an in_transit to_house item with requests."""
    item, patron, loan = item2_in_transit_martigny_patron_and_loan_to_house
    # the following tests the circulation action CANCEL_REQUEST_5_1_2
    # an item in_transit to house with pending loans. when a
    # librarian wants to cancel the in_transit loan. action is permitted.
    # the loan will be cancelled. and first pending loan will be validated.
    params = {
        'patron_pid': patron2_martigny.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny.pid,
        'pickup_location_pid': loc_public_martigny.pid
    }
    item, requested_loan = item_record_to_a_specific_loan_state(
        item=item,
        loan_state=LoanState.PENDING,
        params=params,
        copy_item=False)
    assert requested_loan['state'] == LoanState.PENDING

    params = {
        'pid': loan.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny.pid
    }
    item.cancel_item_request(**params)
    item = Item.get_record_by_pid(item.pid)
    loan = Loan.get_record_by_pid(loan.pid)
    requested_loan = Loan.get_record_by_pid(requested_loan.pid)
    assert item.status == ItemStatus.AT_DESK
    assert loan['state'] == LoanState.CANCELLED
    assert requested_loan['state'] == LoanState.ITEM_AT_DESK
Пример #8
0
def test_items_extend_end_date(client, librarian_martigny, patron_martigny,
                               loc_public_martigny,
                               item_type_standard_martigny, item_lib_martigny,
                               json_header, circ_policy_short_martigny):
    """Test correct renewal due date for items."""
    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_user_pid=librarian_martigny.pid,
             transaction_location_pid=loc_public_martigny.pid))
    assert res.status_code == 200
    actions = data.get('action_applied')
    loan_pid = actions[LoanAction.CHECKOUT].get('pid')
    loan = Loan.get_record_by_pid(loan_pid)
    assert not item.get_extension_count()

    max_count = get_extension_params(loan=loan, parameter_name='max_count')
    renewal_duration_policy = circ_policy_short_martigny['renewal_duration']
    renewal_duration = get_extension_params(loan=loan,
                                            parameter_name='duration_default')
    assert renewal_duration_policy <= renewal_duration.days

    # extend loan
    res, data = postdata(
        client, 'api_item.extend_loan',
        dict(item_pid=item_pid,
             pid=loan_pid,
             transaction_user_pid=librarian_martigny.pid,
             transaction_location_pid=loc_public_martigny.pid))

    assert res.status_code == 200

    # Compare expected loan date with processed one
    # first get loan UTC date
    actions = data.get('action_applied')
    loan_pid = actions[LoanAction.EXTEND].get('pid')
    loan = Loan.get_record_by_pid(loan_pid)
    loan_date = loan.get('end_date')
    # then process a date with current UTC date + renewal
    current_date = datetime.now(timezone.utc)
    calc_date = current_date + renewal_duration
    # finally the comparison should give the same date (in UTC)!
    assert (calc_date.strftime('%Y-%m-%d') == ciso8601.parse_datetime(
        loan_date).astimezone(timezone.utc).strftime('%Y-%m-%d'))

    # checkin
    res, _ = postdata(
        client, 'api_item.checkin',
        dict(item_pid=item_pid,
             pid=loan_pid,
             transaction_user_pid=librarian_martigny.pid,
             transaction_location_pid=loc_public_martigny.pid))
    assert res.status_code == 200
Пример #9
0
def test_cancel_pending_request_on_item_at_desk(
        client, item5_at_desk_martigny_patron_and_loan_at_desk,
        loc_public_martigny, librarian_martigny_no_email,
        patron2_martigny_no_email):
    """Test cancel requests on an at_desk item with requests at home."""
    item, patron, loan = item5_at_desk_martigny_patron_and_loan_at_desk
    # the following tests the circulation action CANCEL_REQUEST_2_2
    # an item at_desk with other pending loans. when a librarian wants to
    # cancel one of the pending loans. the item remains at_desk
    params = {
        'patron_pid': patron2_martigny_no_email.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny_no_email.pid,
        'pickup_location_pid': loc_public_martigny.pid
    }
    item, requested_loan = item_record_to_a_specific_loan_state(
        item=item,
        loan_state=LoanState.PENDING,
        params=params,
        copy_item=False)
    assert requested_loan['state'] == LoanState.PENDING

    params = {
        'pid': requested_loan.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny_no_email.pid
    }
    item.cancel_item_request(**params)
    item = Item.get_record_by_pid(item.pid)
    loan = Loan.get_record_by_pid(loan.pid)
    requested_loan = Loan.get_record_by_pid(requested_loan.pid)
    assert item.status == ItemStatus.AT_DESK
    assert requested_loan['state'] == LoanState.CANCELLED
    assert loan['state'] == LoanState.ITEM_AT_DESK
Пример #10
0
def test_checkin_on_item_on_loan_with_requests_externally(
        item4_on_loan_martigny_patron_and_loan_on_loan,
        item5_on_loan_martigny_patron_and_loan_on_loan,
        loc_public_martigny, librarian_martigny,
        patron2_martigny, loc_public_fully, loc_public_saxon):
    """Test checkin on an on_loan item with requests at an external library."""
    item, patron, loan = item4_on_loan_martigny_patron_and_loan_on_loan
    # the following tests the circulation action CHECKIN_3_2_2_1
    # for an item on_loan, with pending requests. when the pickup library of
    # the first pending request does not equal to the transaction library,
    # checkin the item and the loan on_loan is cancelled.
    # if the pickup location of the first pending equal the item library,
    # the pending loan becomes ITEM_IN_TRANSIT_FOR_PICKUP

    params = {
        'patron_pid': patron2_martigny.pid,
        'transaction_location_pid': loc_public_fully.pid,
        'transaction_user_pid': librarian_martigny.pid,
        'pickup_location_pid': loc_public_martigny.pid
    }

    item, requested_loan = item_record_to_a_specific_loan_state(
        item=item, loan_state=LoanState.PENDING, params=params,
        copy_item=False)

    item, actions = item.checkin(**params)
    item = Item.get_record_by_pid(item.pid)
    loan = Loan.get_record_by_pid(loan.pid)
    requested_loan = Loan.get_record_by_pid(requested_loan.pid)
    assert item.status == ItemStatus.IN_TRANSIT
    assert loan['state'] == LoanState.CANCELLED
    assert requested_loan['state'] == LoanState.ITEM_IN_TRANSIT_FOR_PICKUP

    item, patron, loan = item5_on_loan_martigny_patron_and_loan_on_loan
    # the following tests the circulation action CHECKIN_3_2_2_2
    # for an item on_loan, with pending requests. when the pickup library of
    # the first pending request does not equal to the transaction library,
    # checkin the item and the loan on_loan is cancelled.
    # if the pickup location of the first pending does not equal the item
    # library, the pending loan becomes ITEM_IN_TRANSIT_FOR_PICKUP

    params = {
        'patron_pid': patron2_martigny.pid,
        'transaction_location_pid': loc_public_saxon.pid,
        'transaction_user_pid': librarian_martigny.pid,
        'pickup_location_pid': loc_public_fully.pid
    }

    item, requested_loan = item_record_to_a_specific_loan_state(
        item=item, loan_state=LoanState.PENDING, params=params,
        copy_item=False)

    item, actions = item.checkin(**params)
    item = Item.get_record_by_pid(item.pid)
    loan = Loan.get_record_by_pid(loan.pid)
    requested_loan = Loan.get_record_by_pid(requested_loan.pid)
    assert item.status == ItemStatus.IN_TRANSIT
    assert loan['state'] == LoanState.CANCELLED
    assert requested_loan['state'] == LoanState.ITEM_IN_TRANSIT_FOR_PICKUP
Пример #11
0
def item_record_to_a_specific_loan_state(item=None,
                                         loan_state=None,
                                         params=None,
                                         copy_item=True):
    """Put an item into a specific circulation loan state.

    :param item: the item record
    :param loan_state: the desired loan state and attached to the given item
    :param params: the required parameters to perform the circ transactions
    :param copy_item: an option to perform transaction on a copy of the item

    :return: the item and its loan
    """
    if copy_item:
        item = create_new_item_from_existing_item(item=item)

    # complete missing parameters
    params.setdefault('transaction_date',
                      datetime.now(timezone.utc).isoformat())
    params.setdefault('document_pid', item.document_pid)

    # a parameter to allow in_transit returns
    checkin_transaction_location_pid = \
        params.pop('checkin_transaction_location_pid', None)
    patron = Patron.get_record_by_pid(params.get('patron_pid'))
    # perform circulation actions
    if loan_state in [
            LoanState.PENDING, LoanState.ITEM_AT_DESK, LoanState.ITEM_ON_LOAN,
            LoanState.ITEM_IN_TRANSIT_FOR_PICKUP,
            LoanState.ITEM_IN_TRANSIT_TO_HOUSE
    ]:
        item, actions = item.request(**params)
        loan = Loan.get_record_by_pid(actions[LoanAction.REQUEST].get('pid'))
        assert item.number_of_requests() >= 1
        assert item.is_requested_by_patron(
            patron.get('patron', {}).get('barcode'))
    if loan_state in [
            LoanState.ITEM_AT_DESK, LoanState.ITEM_IN_TRANSIT_FOR_PICKUP,
            LoanState.ITEM_IN_TRANSIT_TO_HOUSE
    ]:
        item, actions = item.validate_request(**params, pid=loan.pid)
        loan = Loan.get_record_by_pid(actions[LoanAction.VALIDATE].get('pid'))
    if loan_state in [
            LoanState.ITEM_ON_LOAN, LoanState.ITEM_IN_TRANSIT_TO_HOUSE
    ]:
        item, actions = item.checkout(**params, pid=loan.pid)
        loan = Loan.get_record_by_pid(actions[LoanAction.CHECKOUT].get('pid'))
    if loan_state == LoanState.ITEM_IN_TRANSIT_TO_HOUSE:
        if checkin_transaction_location_pid:
            params['transaction_location_pid'] = \
                checkin_transaction_location_pid
        item, actions = item.checkin(**params, pid=loan.pid)
        loan = Loan.get_record_by_pid(actions[LoanAction.CHECKIN].get('pid'))

    flush_index(ItemsSearch.Meta.index)
    flush_index(LoansSearch.Meta.index)

    assert loan['state'] == loan_state
    return item, loan
Пример #12
0
def test_item_information(client, librarian_martigny,
                          selfcheck_patron_martigny, loc_public_martigny,
                          item_lib_martigny, circulation_policies):
    """Test item information."""
    login_user_via_session(client, librarian_martigny.user)
    # checkout
    res, data = postdata(
        client, 'api_item.checkout',
        dict(item_pid=item_lib_martigny.pid,
             patron_pid=selfcheck_patron_martigny.pid,
             transaction_user_pid=librarian_martigny.pid,
             transaction_location_pid=loc_public_martigny.pid))
    assert res.status_code == 200
    actions = data.get('action_applied')
    loan_pid = actions[LoanAction.CHECKOUT].get('pid')
    loan = Loan.get_record_by_pid(loan_pid)

    assert not loan.is_loan_overdue()
    # set loan on overdue
    end_date = datetime.now(timezone.utc) - timedelta(days=7)
    loan['end_date'] = end_date.isoformat()
    loan.update(loan, dbcommit=True, reindex=True)
    loan = Loan.get_record_by_pid(loan_pid)
    assert loan['state'] == LoanState.ITEM_ON_LOAN
    assert loan.is_loan_overdue()
    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

    patron_barcode = selfcheck_patron_martigny\
        .get('patron', {}).get('barcode')[0]
    item_pid = item_lib_martigny.pid

    # get item information
    response = item_information(patron_barcode=patron_barcode,
                                item_pid=item_pid)
    assert response
    # check required fields in response
    assert all(key in response for key in (
        'item_id',
        'title_id',
        'circulation_status',
        'fee_type',
        'security_marker',
    ))
    assert response['due_date']
    assert response['fee_amount']

    # checkin
    res, _ = postdata(
        client, 'api_item.checkin',
        dict(item_pid=item_lib_martigny.pid,
             pid=loan_pid,
             transaction_user_pid=librarian_martigny.pid,
             transaction_location_pid=loc_public_martigny.pid))
    assert res.status_code == 200
Пример #13
0
def test_checkout_item_transit(client, item2_lib_martigny,
                               librarian_martigny_no_email,
                               librarian_saxon_no_email,
                               patron_martigny_no_email, loc_public_saxon,
                               circulation_policies):
    """Test checkout of an item in transit."""
    assert item2_lib_martigny.available

    # request
    login_user_via_session(client, librarian_martigny_no_email.user)

    res, data = postdata(
        client, 'api_item.librarian_request',
        dict(item_pid=item2_lib_martigny.pid,
             pickup_location_pid=loc_public_saxon.pid,
             patron_pid=patron_martigny_no_email.pid))
    assert res.status_code == 200
    actions = data.get('action_applied')
    loan_pid = actions[LoanAction.REQUEST].get('pid')
    assert not item2_lib_martigny.available

    loan = Loan.get_record_by_pid(loan_pid)
    assert loan.get('state') == 'PENDING'

    # validate request
    res, _ = postdata(client, 'api_item.validate_request',
                      dict(item_pid=item2_lib_martigny.pid, pid=loan_pid))
    assert res.status_code == 200
    assert not item2_lib_martigny.available
    item = Item.get_record_by_pid(item2_lib_martigny.pid)
    assert not item.available

    loan = Loan.get_record_by_pid(loan_pid)
    assert loan.get('state') == 'ITEM_IN_TRANSIT_FOR_PICKUP'

    login_user_via_session(client, librarian_saxon_no_email.user)
    # receive
    res, _ = postdata(client, 'api_item.receive',
                      dict(item_pid=item2_lib_martigny.pid, pid=loan_pid))
    assert res.status_code == 200
    assert not item2_lib_martigny.available
    item = Item.get_record_by_pid(item2_lib_martigny.pid)
    assert not item.available

    loan_before_checkout = get_loan_for_item(item.pid)
    assert loan_before_checkout.get('state') == 'ITEM_AT_DESK'
    # checkout
    res, _ = postdata(
        client, 'api_item.checkout',
        dict(item_pid=item2_lib_martigny.pid,
             patron_pid=patron_martigny_no_email.pid))
    assert res.status_code == 200
    item = Item.get_record_by_pid(item2_lib_martigny.pid)
    loan_after_checkout = get_loan_for_item(item.pid)
    assert loan_after_checkout.get('state') == 'ITEM_ON_LOAN'
    assert loan_before_checkout.get('pid') == loan_after_checkout.get('pid')
Пример #14
0
def test_multiple_loans_on_item_error(
        client, patron_martigny_no_email, patron2_martigny_no_email,
        loc_public_martigny, item_type_standard_martigny, item_lib_martigny,
        json_header, circulation_policies, loc_public_fully,
        librarian_martigny_no_email):
    """Test MultipleLoansOnItemError."""
    login_user_via_session(client, librarian_martigny_no_email.user)
    item = item_lib_martigny
    checked_patron = patron2_martigny_no_email.pid
    requested_patron = patron_martigny_no_email.pid
    location = loc_public_martigny
    # checkout to checked_patron
    res, data = postdata(
        client, 'api_item.checkout',
        dict(item_pid=item.pid,
             patron_pid=checked_patron,
             transaction_location_pid=location.pid))
    assert res.status_code == 200
    assert Item.get_record_by_pid(item.pid).get('status') == ItemStatus.ON_LOAN
    item_data = data.get('metadata')
    actions = data.get('action_applied')
    assert item_data.get('status') == ItemStatus.ON_LOAN
    assert actions.get(LoanAction.CHECKOUT)
    loan_pid = actions[LoanAction.CHECKOUT].get('pid')
    item = Item.get_record_by_pid(item.pid)

    # request by requested patron to pick at another location
    res, data = postdata(
        client, 'api_item.librarian_request',
        dict(item_pid=item.pid,
             pickup_location_pid=loc_public_fully.pid,
             patron_pid=requested_patron))
    assert res.status_code == 200
    item_data = data.get('metadata')
    actions = data.get('action_applied')
    assert item_data.get('status') == ItemStatus.ON_LOAN
    assert actions.get(LoanAction.REQUEST)
    req_loan_pid = actions[LoanAction.REQUEST].get('pid')
    item = Item.get_record_by_pid(item.pid)

    # checkin at the request location
    res, data = postdata(
        client, 'api_item.checkin',
        dict(item_pid=item.pid,
             pid=loan_pid,
             transaction_location_pid=loc_public_fully.pid))
    assert res.status_code == 200
    assert Loan.get_record_by_pid(loan_pid).get('state') == 'CANCELLED'
    assert Loan.get_record_by_pid(req_loan_pid).get(
        'state') == 'ITEM_IN_TRANSIT_FOR_PICKUP'
    assert Item.get_record_by_pid(item.pid).get('status') == 'in_transit'
    # cancel request
    res, _ = postdata(client, 'api_item.cancel_loan',
                      dict(item_pid=item.pid, pid=req_loan_pid))
    assert res.status_code == 200
Пример #15
0
def test_items_extend_end_date(client, librarian_martigny_no_email,
                               patron_martigny_no_email, loc_public_martigny,
                               item_type_standard_martigny, item_lib_martigny,
                               json_header, circ_policy_short_martigny):
    """Test correct renewal due date for items."""
    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)
    actions = data.get('action_applied')
    loan_pid = actions[LoanAction.CHECKOUT].get('pid')
    loan = Loan.get_record_by_pid(loan_pid)
    assert not item.get_extension_count()

    max_count = get_extension_params(loan=loan, parameter_name='max_count')
    renewal_duration_policy = circ_policy_short_martigny['renewal_duration']
    renewal_duration = get_extension_params(loan=loan,
                                            parameter_name='duration_default')
    assert renewal_duration_policy <= renewal_duration.days

    # extend loan
    res = client.post(
        url_for('api_item.extend_loan'),
        data=json.dumps(dict(item_pid=item_pid, pid=loan_pid)),
        content_type='application/json',
    )

    assert res.status_code == 200
    data = get_json(res)
    actions = data.get('action_applied')
    loan_pid = actions[LoanAction.EXTEND].get('pid')
    loan = Loan.get_record_by_pid(loan_pid)
    end_date = loan.get('end_date')
    current_date = datetime.now()
    calc_date = current_date + renewal_duration
    assert (calc_date.strftime('%Y-%m-%d') == ciso8601.parse_datetime_as_naive(
        end_date).strftime('%Y-%m-%d'))

    # checkin
    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
Пример #16
0
def test_checkin_on_item_on_loan_with_requests(
        item3_on_loan_martigny_patron_and_loan_on_loan,
        loc_public_martigny, librarian_martigny,
        patron2_martigny):
    """Test checkin on an on_loan item with requests at local library."""
    # the following tests the circulation action CHECKIN_3_2_1
    # for an item on_loan, with pending requests. when the pickup library of
    # the first pending request equal to the transaction library,
    # checkin the item and item becomes at_desk.
    # the on_loan is returned and validating the first pending loan request.
    #
    # In this test, we will also ensure that the request expiration date of the
    # automatic validated request is correct
    item, patron, loan = item3_on_loan_martigny_patron_and_loan_on_loan

    # create a request on the same item one day after the first loan
    tomorrow = ciso8601.parse_datetime(loan['start_date']) + timedelta(days=10)
    with freeze_time(tomorrow.isoformat()):
        item, actions = item.request(
            pickup_location_pid=loc_public_martigny.pid,
            patron_pid=patron2_martigny.pid,
            transaction_location_pid=loc_public_martigny.pid,
            transaction_user_pid=librarian_martigny.pid
        )
        requested_loan_pid = actions[LoanAction.REQUEST].get('pid')
        requested_loan = Loan.get_record_by_pid(requested_loan_pid)

    # Check-in the item
    #  * reload item, loan and requested_loan
    #  * ensure the item is still AT_DESK (because the first pending request
    #    has been automatically validate and pickup location is the same than
    #    previous loan location)
    #  * ensure first loan is concluded
    #  * ensure the requested loan is now "AT_DESK" with a valid request
    #    expiration date
    next_day = tomorrow + timedelta(days=10)
    with freeze_time(next_day.isoformat()):
        item, actions = item.checkin(
            patron_pid=patron2_martigny.pid,
            transaction_location_pid=loc_public_martigny.pid,
            transaction_user_pid=librarian_martigny.pid,
            pickup_location_pid=loc_public_martigny.pid
        )

    item = Item.get_record_by_pid(item.pid)
    loan = Loan.get_record_by_pid(loan.pid)
    requested_loan = Loan.get_record_by_pid(requested_loan.pid)

    assert item.status == ItemStatus.AT_DESK
    assert loan['state'] == LoanState.ITEM_RETURNED
    assert requested_loan['state'] == LoanState.ITEM_AT_DESK
    trans_date = ciso8601.parse_datetime(requested_loan['transaction_date'])
    assert trans_date.strftime('%Y%m%d') == next_day.strftime('%Y%m%d')
Пример #17
0
def test_patron_information(client, librarian_martigny,
                            selfcheck_patron_martigny, loc_public_martigny,
                            item_lib_martigny, item2_lib_martigny,
                            circulation_policies, lib_martigny):
    """Test patron information."""
    login_user_via_session(client, librarian_martigny.user)
    # checkout
    res, data = postdata(
        client, 'api_item.checkout',
        dict(item_pid=item_lib_martigny.pid,
             patron_pid=selfcheck_patron_martigny.pid,
             transaction_user_pid=librarian_martigny.pid,
             transaction_location_pid=loc_public_martigny.pid))
    assert res.status_code == 200
    actions = data.get('action_applied')
    loan_pid = actions[LoanAction.CHECKOUT].get('pid')
    loan = Loan.get_record_by_pid(loan_pid)
    assert not loan.is_loan_overdue()
    # set loan on overdue
    end_date = datetime.now(timezone.utc) - timedelta(days=7)
    loan['end_date'] = end_date.isoformat()
    loan.update(loan, dbcommit=True, reindex=True)
    loan = Loan.get_record_by_pid(loan_pid)
    assert loan.is_loan_overdue()
    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
    # create request
    res, data = postdata(
        client, 'api_item.librarian_request',
        dict(item_pid=item2_lib_martigny.pid,
             patron_pid=selfcheck_patron_martigny.pid,
             pickup_location_pid=loc_public_martigny.pid,
             transaction_library_pid=lib_martigny.pid,
             transaction_user_pid=librarian_martigny.pid))
    assert res.status_code == 200
    # get patron information
    response = patron_information(
        selfcheck_patron_martigny.get('patron', {}).get('barcode')[0])
    assert response

    # checkin
    res, _ = postdata(
        client, 'api_item.checkin',
        dict(item_pid=item_lib_martigny.pid,
             pid=loan_pid,
             transaction_user_pid=librarian_martigny.pid,
             transaction_location_pid=loc_public_martigny.pid))
    assert res.status_code == 200
Пример #18
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)
Пример #19
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,
        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.pid
    }
    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.user.profile.keep_history = False
    # 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.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_notifications(types=[
        Notification.DUE_SOON_NOTIFICATION_TYPE,
        Notification.OVERDUE_NOTIFICATION_TYPE
    ])
    flush_index(NotificationsSearch.Meta.index)
    flush_index(LoansSearch.Meta.index)

    params = {
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny.pid
    }
    item.checkin(**params)
    loan = Loan.get_record_by_pid(loan.pid)

    assert not loan.concluded(loan)
    assert not loan.can_anonymize(loan_data=loan)
Пример #20
0
def test_cancel_item_request_on_item_on_loan(
        client, item_on_loan_martigny_patron_and_loan_on_loan,
        loc_public_martigny, librarian_martigny_no_email,
        patron2_martigny_no_email):
    """Test cancel requests on an on_loan item."""
    item, patron, loan = item_on_loan_martigny_patron_and_loan_on_loan
    # the following tests the circulation action CANCEL_REQUEST_3_1
    # an item on_loan with no other pending loans. when a librarian wants to
    # cancel the on_loan loan. action is not permitted and item remain on_loan.
    params = {
        'pid': loan.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny_no_email.pid
    }
    with pytest.raises(NoCirculationAction):
        item.cancel_item_request(**params)
    item = Item.get_record_by_pid(item.pid)
    loan = Loan.get_record_by_pid(loan.pid)
    assert item.status == ItemStatus.ON_LOAN
    assert loan['state'] == LoanState.ITEM_ON_LOAN

    # the following tests the circulation action CANCEL_REQUEST_3_2
    # an item on_loan with other pending loans. when a librarian wants to
    # cancel the pending loan. action is permitted and item remains on_loan.
    params = {
        'patron_pid': patron2_martigny_no_email.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny_no_email.pid,
        'pickup_location_pid': loc_public_martigny.pid
    }
    item, requested_loan = item_record_to_a_specific_loan_state(
        item=item,
        loan_state=LoanState.PENDING,
        params=params,
        copy_item=False)
    assert requested_loan['state'] == LoanState.PENDING

    params = {
        'pid': requested_loan.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny_no_email.pid
    }
    item.cancel_item_request(**params)
    item = Item.get_record_by_pid(item.pid)
    loan = Loan.get_record_by_pid(loan.pid)
    requested_loan = Loan.get_record_by_pid(requested_loan.pid)
    assert item.status == ItemStatus.ON_LOAN
    assert loan['state'] == LoanState.ITEM_ON_LOAN
    assert requested_loan['state'] == LoanState.CANCELLED
Пример #21
0
def test_checkin_on_item_on_loan(
        item_on_loan_martigny_patron_and_loan_on_loan,
        item2_on_loan_martigny_patron_and_loan_on_loan,
        item_on_loan_fully_patron_and_loan_on_loan, loc_public_fully,
        loc_public_martigny, librarian_martigny):
    """Test checkin on an on_loan item."""
    item, patron, loan = item_on_loan_martigny_patron_and_loan_on_loan
    # the following tests the circulation action CHECKIN_3_1_1
    # for an item on_loan, the item library equal the transaction library,
    # checkin the item and item becomes on_shelf
    # case when the loan pid is given as a parameter
    params = {
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny.pid,
        'pid': loan.pid
    }
    item, actions = item.checkin(**params)
    item = Item.get_record_by_pid(item.pid)
    loan = Loan.get_record_by_pid(loan.pid)
    assert item.status == ItemStatus.ON_SHELF
    assert loan['state'] == LoanState.ITEM_RETURNED

    # case when the loan pid is not given as a parameter
    item, patron, loan = item_on_loan_fully_patron_and_loan_on_loan
    params = {
        'transaction_location_pid': loc_public_fully.pid,
        'transaction_user_pid': librarian_martigny.pid
    }
    item, actions = item.checkin(**params)
    item = Item.get_record_by_pid(item.pid)
    loan = Loan.get_record_by_pid(loan.pid)
    assert item.status == ItemStatus.ON_SHELF
    assert loan['state'] == LoanState.ITEM_RETURNED

    # the following tests the circulation action CHECKIN_3_1_2
    # for an item on_loan, the item library does not equal the transaction
    # library, checkin the item and item becomes in_transit
    item, patron, loan = item2_on_loan_martigny_patron_and_loan_on_loan
    params = {
        'transaction_location_pid': loc_public_fully.pid,
        'transaction_user_pid': librarian_martigny.pid,
        'pid': loan.pid
    }
    item, actions = item.checkin(**params)
    item = Item.get_record_by_pid(item.pid)
    loan = Loan.get_record_by_pid(loan.pid)
    assert item.status == ItemStatus.IN_TRANSIT
    assert loan['state'] == LoanState.ITEM_IN_TRANSIT_TO_HOUSE
Пример #22
0
def test_checkout_on_item_in_transit_to_house_for_another_patron(
        item2_in_transit_martigny_patron_and_loan_to_house,
        patron2_martigny,
        librarian_martigny,
        loc_public_martigny,
        loc_public_saxon):
    """Test CHECKOUT on an IN_TRANSIT (to house) item."""
    # Create a new item in IN_TRANSIT_TO_HOUSE
    intransit_item, patron, loan = \
        item2_in_transit_martigny_patron_and_loan_to_house
    assert intransit_item.number_of_requests() == 0

    # the following tests the circulation action CHECKOUT_5_1
    # an IN_TRANSIT (to house) item
    # WITHOUT pending loan
    # CAN be CHECKOUT
    params = {
        'patron_pid': patron2_martigny.pid,
        'transaction_location_pid': loc_public_saxon.pid,
        'transaction_user_pid': librarian_martigny.pid,
        'pickup_location_pid': loc_public_martigny.pid,
    }
    # Checkout it!
    asked_item, actions = intransit_item.checkout(**params, pid=loan.pid)
    checkout_loan = Loan.get_record_by_pid(
        actions[LoanAction.CHECKOUT].get('pid'))
    # Check loan is ITEM_ON_LOAN and item is ON_LOAN
    assert intransit_item.status == ItemStatus.ON_LOAN
    assert checkout_loan['state'] == LoanState.ITEM_ON_LOAN
    assert intransit_item.number_of_requests() == 0
Пример #23
0
def loan_overdue_saxon(
        app,
        document,
        item2_lib_saxon,
        loc_public_martigny,
        item_type_standard_martigny,
        librarian_martigny_no_email,
        patron_martigny_no_email,
        circulation_policies):
    """Checkout an item to a patron.

    item2_lib_saxon is overdue.
    """
    transaction_date = datetime.now(timezone.utc).isoformat()

    item2_lib_saxon.checkout(
        patron_pid=patron_martigny_no_email.pid,
        transaction_location_pid=loc_public_martigny.pid,
        transaction_user_pid=librarian_martigny_no_email.pid,
        transaction_date=transaction_date,
        document_pid=item2_lib_saxon.replace_refs()['document']['pid']
    )
    flush_index(ItemsSearch.Meta.index)
    flush_index(LoansSearch.Meta.index)
    loan = Loan.get_record_by_pid(
        item2_lib_saxon.get_loan_pid_with_item_on_loan(
            item2_lib_saxon.pid))
    end_date = datetime.now(timezone.utc) - timedelta(days=25)
    loan['end_date'] = end_date.isoformat()
    loan = loan.update(loan, dbcommit=True, reindex=True)
    return loan
def test_cancel_request_on_item_on_shelf(
        item_lib_martigny, item_on_shelf_martigny_patron_and_loan_pending,
        loc_public_martigny, librarian_martigny_no_email):
    """Test cancel request on an on_shelf item."""
    # the following tests the circulation action CANCEL_REQUEST_1_1
    # on_shelf item with no pending requests, not possible to cancel a request.
    # a loan pid is a required parameter and not given
    with pytest.raises(TypeError):
        item_lib_martigny.cancel_item_request()
    assert item_lib_martigny.status == ItemStatus.ON_SHELF

    # the following tests the circulation action CANCEL_REQUEST_1_2
    # on_shelf item with pending requests, cancel a pending loan is possible.
    # the item remains on_shelf
    item, patron, loan = item_on_shelf_martigny_patron_and_loan_pending
    params = {
        'pid': loan.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny_no_email.pid
    }
    item.cancel_item_request(**params)
    item = Item.get_record_by_pid(item.pid)
    loan = Loan.get_record_by_pid(loan.pid)
    assert item.status == ItemStatus.ON_SHELF
    assert loan['state'] == LoanState.CANCELLED
def test_recall_notification(client, patron_martigny_no_email, json_header,
                             patron2_martigny_no_email, item_lib_martigny,
                             librarian_martigny_no_email, circulation_policies,
                             loc_public_martigny):
    """Test recall notification."""
    login_user_via_session(client, librarian_martigny_no_email.user)
    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)

    assert not loan.is_notified(notification_type='recall')
    # test notification permissions
    res, _ = postdata(
        client, 'api_item.librarian_request',
        dict(item_pid=item_lib_martigny.pid,
             pickup_location_pid=loc_public_martigny.pid,
             patron_pid=patron2_martigny_no_email.pid))
    assert res.status_code == 200

    flush_index(NotificationsSearch.Meta.index)

    assert loan.is_notified(notification_type='recall')

    notification = get_recall_notification(loan)
    assert notification.loan_pid == loan.pid

    assert not loan.is_notified(notification_type='availability')
    assert not get_availability_notification(loan)
def upgrade():
    """Update loans records."""
    query = current_circulation.loan_search_cls() \
        .filter('term', state=LoanState.ITEM_AT_DESK) \
        .filter('bool', must_not=[Q('exists', field='request_expire_date')]) \
        .source('pid')
    loan_pids = [hit.pid for hit in query.scan()]
    ids = []
    for pid in loan_pids:
        loan = Loan.get_record_by_pid(pid)
        trans_date = ciso8601.parse_datetime(loan.transaction_date)
        expire_date = trans_date + timedelta(days=10)
        expire_date = expire_date.replace(hour=23,
                                          minute=59,
                                          second=00,
                                          microsecond=000,
                                          tzinfo=None)
        expire_date = pytz.timezone('Europe/Zurich').localize(expire_date)
        loan['request_expire_date'] = expire_date.isoformat()
        loan['request_start_date'] = datetime.now().isoformat()
        loan.update(loan, dbcommit=True, reindex=False)
        LOGGER.info(f'  * Updated loan#{loan.pid}')
        ids.append(loan.id)
    if len(ids):
        LOGGER.info(f'Indexing {len(ids)} records ....')
        indexer = LoansIndexer()
        indexer.bulk_index(ids)
        count = indexer.process_bulk_queue()
        LOGGER.info(f'{count} records indexed.')
    LOGGER.info(f'TOTAL :: {len(ids)}')
Пример #27
0
def loan_overdue_martigny(app, document, item4_lib_martigny,
                          loc_public_martigny, item_type_standard_martigny,
                          librarian_martigny, patron_martigny,
                          circulation_policies):
    """Checkout an item to a patron.

    item4_lib_martigny is overdue.
    """
    transaction_date = datetime.now(timezone.utc).isoformat()

    item4_lib_martigny.checkout(
        patron_pid=patron_martigny.pid,
        transaction_location_pid=loc_public_martigny.pid,
        transaction_user_pid=librarian_martigny.pid,
        transaction_date=transaction_date,
        document_pid=extracted_data_from_ref(
            item4_lib_martigny.get('document')))
    flush_index(ItemsSearch.Meta.index)
    flush_index(LoansSearch.Meta.index)
    loan = Loan.get_record_by_pid(
        item4_lib_martigny.get_loan_pid_with_item_on_loan(
            item4_lib_martigny.pid))
    end_date = datetime.now(timezone.utc) - timedelta(days=25)
    loan['end_date'] = end_date.isoformat()
    loan = loan.update(loan, dbcommit=True, reindex=True)
    return loan
Пример #28
0
def test_items_extend_end_date(client, librarian_martigny_no_email,
                               patron_martigny_no_email, loc_public_martigny,
                               item_type_standard_martigny, item_lib_martigny,
                               json_header, circ_policy_short_martigny):
    """Test correct renewal due date for items."""
    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))
    assert res.status_code == 200
    actions = data.get('action_applied')
    loan_pid = actions[LoanAction.CHECKOUT].get('pid')
    loan = Loan.get_record_by_pid(loan_pid)
    assert not item.get_extension_count()

    max_count = get_extension_params(loan=loan, parameter_name='max_count')
    renewal_duration_policy = circ_policy_short_martigny['renewal_duration']
    renewal_duration = get_extension_params(loan=loan,
                                            parameter_name='duration_default')
    assert renewal_duration_policy <= renewal_duration.days

    # extend loan
    res, data = postdata(client, 'api_item.extend_loan',
                         dict(item_pid=item_pid, pid=loan_pid))

    assert res.status_code == 200
    actions = data.get('action_applied')
    loan_pid = actions[LoanAction.EXTEND].get('pid')
    loan = Loan.get_record_by_pid(loan_pid)
    end_date = loan.get('end_date')
    current_date = datetime.now(timezone.utc)
    calc_date = current_date + renewal_duration
    # TODO: check whey this test fails with this error @travis
    # AssertionError: assert '2019-10-11' == '2019-10-10'
    # assert (
    #     calc_date.strftime('%Y-%m-%d') == ciso8601.parse_datetime(
    #         end_date).strftime('%Y-%m-%d')
    # )

    # checkin
    res, _ = postdata(client, 'api_item.checkin',
                      dict(item_pid=item_pid, pid=loan_pid))
    assert res.status_code == 200
Пример #29
0
def test_in_transit_second_request_at_home(app, item_lib_martigny,
                                           patron_martigny, patron2_martigny,
                                           librarian_martigny,
                                           loc_public_martigny,
                                           circulation_policies,
                                           loc_public_fully):
    """Test cases when in-transit loan is cancelled."""
    params = {
        'patron_pid': patron_martigny.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny.pid,
        'pickup_location_pid': loc_public_fully.pid
    }
    assert item_lib_martigny.status == ItemStatus.ON_SHELF
    item, actions = item_lib_martigny.request(**params)
    first_loan = Loan.get_record_by_pid(actions[LoanAction.REQUEST].get('pid'))
    assert item_lib_martigny.status == ItemStatus.ON_SHELF
    assert first_loan['state'] == LoanState.PENDING
    item, actions = item.validate_request(**params, pid=first_loan.pid)
    first_loan = Loan.get_record_by_pid(
        actions[LoanAction.VALIDATE].get('pid'))
    assert item.status == ItemStatus.IN_TRANSIT
    assert first_loan['state'] == LoanState.ITEM_IN_TRANSIT_FOR_PICKUP

    params = {
        'patron_pid': patron2_martigny.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny.pid,
        'pickup_location_pid': loc_public_martigny.pid
    }
    item, actions = item.request(**params)
    second_loan = Loan.get_record_by_pid(
        actions[LoanAction.REQUEST].get('pid'))
    assert item_lib_martigny.status == ItemStatus.IN_TRANSIT
    assert second_loan['state'] == LoanState.PENDING

    params = {
        'pid': first_loan.pid,
        'transaction_location_pid': loc_public_martigny.pid,
        'transaction_user_pid': librarian_martigny.pid
    }
    item, actions = item.cancel_item_request(**params)
    first_loan = Loan.get_record_by_pid(first_loan.pid)
    second_loan = Loan.get_record_by_pid(second_loan.pid)
    assert item_lib_martigny.status == ItemStatus.IN_TRANSIT
    assert second_loan['state'] == LoanState.ITEM_IN_TRANSIT_FOR_PICKUP
    assert first_loan['state'] == LoanState.CANCELLED
Пример #30
0
def preview_loan_overdue(loan_pid):
    """HTTP GET request for overdue preview about a loan."""
    loan = Loan.get_record_by_pid(loan_pid)
    if not loan:
        abort(404, 'Loan not found')
    fees = loan.get_overdue_fees
    fees = [(fee[0], fee[1].isoformat()) for fee in fees]  # format date
    return jsonify({'total': sum_for_fees(fees), 'steps': fees})