def test_loan_can_be_created_on_missing_item_by_force(client, json_headers,
                                                      users, testdata):
    """Test that a patron can request a loan on a missing
       item by force checkout."""
    for user in [users['admin'], users['librarian']]:
        login_user_via_session(client, email=User.query.get(user.id).email)
        url = url_for('invenio_app_ils_circulation.loan_create')

        item_exists = True if search_by_pid(
            item_pid=NEW_FORCED_LOAN["item_pid"]) \
            .execute().hits.total > 0 else False

        if item_exists and patron_has_active_loan_on_item(
                patron_pid=NEW_FORCED_LOAN["patron_pid"],
                item_pid=NEW_FORCED_LOAN["item_pid"]):

            res = client.post(url,
                              headers=json_headers,
                              data=json.dumps(NEW_FORCED_LOAN))
            assert res.status_code == 400
            current_item_status = Item.get_record_by_pid(
                NEW_FORCED_LOAN["item_pid"])["status"]
            assert current_item_status == Item.get_record_by_pid(
                NEW_FORCED_LOAN["item_pid"])["status"]
        elif item_exists:
            res = client.post(url,
                              headers=json_headers,
                              data=json.dumps(NEW_FORCED_LOAN))
            assert res.status_code == 202
            loan = json.loads(res.data.decode('utf-8'))['metadata']
            assert loan['state'] == 'ITEM_ON_LOAN'
Example #2
0
def index_after_loan_replace_item(_, old_item_pid, new_item_pid):
    """Register Circulation signal to index item."""
    if old_item_pid:
        item = Item.get_record_by_pid(old_item_pid)
        current_app_ils_extension.item_indexer.index(item)

    if new_item_pid:
        item = Item.get_record_by_pid(new_item_pid)
        current_app_ils_extension.item_indexer.index(item)
Example #3
0
def test_checkout_conditions_librarian(client, json_headers, users, testdata):
    """Test checkout conditions user."""
    librarian = users["librarian"]
    user_login(client, "librarian", users)
    url = url_for("invenio_app_ils_circulation.loan_checkout")

    params = deepcopy(NEW_LOAN)
    params["item_pid"] = dict(type="pitmid", value="itemid-61")
    params["transaction_user_pid"] = str(librarian.id)
    res = client.post(url, headers=json_headers, data=json.dumps(params))
    assert res.status_code == 202

    params = deepcopy(NEW_LOAN)
    params["item_pid"] = dict(type="pitmid", value="itemid-MISSING")
    params["transaction_user_pid"] = str(librarian.id)
    res = client.post(url, headers=json_headers, data=json.dumps(params))
    # missing
    assert res.status_code == 400

    params = deepcopy(NEW_LOAN)
    params["force"] = True
    params["item_pid"] = dict(type="pitmid", value="itemid-MISSING")
    params["transaction_user_pid"] = str(librarian.id)
    res = client.post(url, headers=json_headers, data=json.dumps(params))
    # missing but force
    assert res.status_code == 202
    item = Item.get_record_by_pid(params["item_pid"]["value"])
    assert item["status"] == "CAN_CIRCULATE"
Example #4
0
def create_loan(params, should_force_checkout):
    """Create a loan on behalf of a user."""
    if "patron_pid" not in params:
        raise MissingRequiredParameterError(
            description="'patron_pid' is required when creating a loan")
    if "item_pid" not in params:
        raise MissingRequiredParameterError(
            description="'item_pid' is required when creating a loan")

    if patron_has_active_loan_on_item(patron_pid=params["patron_pid"],
                                      item_pid=params["item_pid"]):
        raise PatronHasLoanOnItemError(params["patron_pid"],
                                       params["item_pid"])

    if "document_pid" not in params:
        document_pid = Item.get_document_pid(params["item_pid"])
        if document_pid:
            params["document_pid"] = document_pid

    if should_force_checkout:
        _ensure_item_can_circulate(params['item_pid'])

    # create a new loan
    record_uuid = uuid.uuid4()
    new_loan = {"item_pid": params["item_pid"]}
    pid = loan_pid_minter(record_uuid, data=new_loan)
    loan = Loan.create(data=new_loan, id_=record_uuid)
    # trigger the first transition
    loan = current_circulation.circulation.trigger(
        loan, **dict(params, trigger="checkout"))

    return pid, loan
Example #5
0
def item_resolver_endpoint(item_pid):
    """Circulation item resolving view function."""
    item = Item.get_record_by_pid(item_pid)
    # To avoid circular dependencies, we remove loan from item.
    del item["circulation_status"]
    del item["document"]
    return item
Example #6
0
def test_update_item_document(client, users, json_headers, testdata, db):
    """Test REPLACE document pid on item."""
    login_user_via_session(client, user=User.query.get(users["admin"].id))
    item = Item.get_record_by_pid("itemid-1")
    item["document_pid"] = "not_found_doc"
    with pytest.raises(ItemDocumentNotFoundError):
        item.commit()
Example #7
0
def _set_item_to_can_circulate(item_pid):
    """Change the item status to CAN_CIRCULATE."""
    item = Item.get_record_by_pid(item_pid["value"])
    if item["status"] != "CAN_CIRCULATE":
        item["status"] = "CAN_CIRCULATE"
        item.commit()
        db.session.commit()
        current_app_ils.item_indexer.index(item)
Example #8
0
def testdata(app, db, es_clear):
    """Create, index and return test data."""
    indexer = RecordIndexer()

    locations = load_json_from_datadir("locations.json")
    for location in locations:
        record = Location.create(location)
        mint_record_pid(LOCATION_PID_TYPE, Location.pid_field, record)
        record.commit()
        db.session.commit()
        indexer.index(record)

    internal_locations = load_json_from_datadir("internal_locations.json")
    iloc_records = []
    for internal_location in internal_locations:
        record = InternalLocation.create(internal_location)
        mint_record_pid(INTERNAL_LOCATION_PID_TYPE, InternalLocation.pid_field,
                        record)
        record.commit()
        iloc_records.append(record)
        db.session.commit()
        indexer.index(record)

    documents = load_json_from_datadir("documents.json")
    for doc in documents:
        record = Document.create(doc)
        mint_record_pid(DOCUMENT_PID_TYPE, Document.pid_field, record)
        record.commit()
        db.session.commit()
        indexer.index(record)

    items = load_json_from_datadir("items.json")
    for item in items:
        record = Item.create(item)
        mint_record_pid(ITEM_PID_TYPE, Item.pid_field, record)
        record.commit()
        db.session.commit()
        indexer.index(record)

    loans = load_json_from_datadir("loans.json")
    for loan in loans:
        record = Loan.create(loan)
        mint_record_pid(CIRCULATION_LOAN_PID_TYPE, Loan.pid_field, record)
        record.commit()
        db.session.commit()
        indexer.index(record)
        # re-index item attached to the loan
        index_record_after_loan_change(app, record)

    # flush all indices after indexing, otherwise ES won't be ready for tests
    current_search.flush_and_refresh(index=None)

    return {
        "locations": locations,
        "documents": documents,
        "items": items,
        "loans": loans,
    }
Example #9
0
def index_item_after_document_indexed(document_pid):
    """Index item to refresh document reference."""
    item_pids = circulation_items_retriever(document_pid)
    item_ids = []
    for pid in item_pids:
        record = Item.get_record_by_pid(pid)
        item_ids.append(record.id)

    RecordIndexer().bulk_index(item_ids)
Example #10
0
def index_document_after_item_indexed(item_pid):
    """Index document to re-compute circulation information."""
    log_func = partial(_log,
                       origin_rec_type='Item',
                       origin_recid=item_pid,
                       dest_rec_type='Document')

    log_func(msg=MSG_ORIGIN)
    document_pid = Item.get_document_pid(item_pid)
    _index_record_by_pid(Document, document_pid, log_func)
Example #11
0
def _ensure_item_can_circulate(item_pid):
    """Change the item status to CAN_CIRCULATE if different."""
    item = Item.get_record_by_pid(item_pid)
    if item["status"] != "CAN_CIRCULATE":
        item = item.patch([{
            'op': 'replace',
            'path': '/status',
            'value': 'CAN_CIRCULATE'
        }])
        item.commit()
        db.session.commit()
        current_app_ils_extension.item_indexer.index(item)
Example #12
0
def _set_item_to_can_circulate(item_pid):
    """Change the item status to CAN_CIRCULATE."""
    item = Item.get_record_by_pid(item_pid)
    if item["status"] != "CAN_CIRCULATE":
        item = item.patch([{
            "op": "replace",
            "path": "/status",
            "value": "CAN_CIRCULATE"
        }])
        item.commit()
        db.session.commit()
        current_app_ils_extension.item_indexer.index(item)
Example #13
0
def testdata(app, db, es_clear, system_user):
    """Create, index and return test data."""
    indexer = RecordIndexer()

    locations = load_json_from_datadir("locations.json")
    for location in locations:
        record = Location.create(location)
        mint_record_pid(LOCATION_PID_TYPE, "pid", record)
        record.commit()
        db.session.commit()
        indexer.index(record)

    internal_locations = load_json_from_datadir("internal_locations.json")
    for internal_location in internal_locations:
        record = InternalLocation.create(internal_location)
        mint_record_pid(INTERNAL_LOCATION_PID_TYPE, "pid", record)
        record.commit()
        db.session.commit()
        indexer.index(record)

    documents = load_json_from_datadir("documents.json")
    for doc in documents:
        record = Document.create(doc)
        mint_record_pid(DOCUMENT_PID_TYPE, "pid", record)
        record.commit()
        db.session.commit()
        indexer.index(record)

    items = load_json_from_datadir("items.json")
    for item in items:
        record = Item.create(item)
        mint_record_pid(ITEM_PID_TYPE, "pid", record)
        record.commit()
        db.session.commit()
        indexer.index(record)

    loans = load_json_from_datadir("loans.json")
    for loan in loans:
        record = Loan.create(loan)
        mint_record_pid(CIRCULATION_LOAN_PID_TYPE, "pid", record)
        record.commit()
        db.session.commit()
        indexer.index(record)

    # flush all indices after indexing, otherwise ES won't be ready for tests
    current_search.flush_and_refresh(index='*')
    return {
        "locations": locations,
        "documents": documents,
        "items": items,
        "loans": loans,
    }
Example #14
0
def item_resolver(loan_pid):
    """Resolve an Item given a Loan PID."""
    try:
        item_pid = get_field_value(Loan, loan_pid, "item_pid")
    except KeyError:
        return {}
    try:
        item = Item.get_record_by_pid(item_pid)
        # remove `Document` and `circulation` fields
        # to avoid circular deps.
        del item["$schema"]
        del item["circulation"]
        del item["document"]
    except PIDDeletedError:
        item = {}
    return item
Example #15
0
def test_update_item_status(client, users, json_headers, testdata, db):
    """Test update item status."""
    def get_active_loan_pid_and_item_pid():
        LoanSearch = current_circulation.loan_search_cls
        for t in testdata["items"]:
            if t["status"] == "CAN_CIRCULATE":
                item_pid = dict(type=ITEM_PID_TYPE, value=t["pid"])
                active_loan = (LoanSearch().get_active_loan_by_item_pid(
                    item_pid).execute().hits)
                total = (active_loan.total
                         if lt_es7 else active_loan.total.value)
                if total > 0:
                    return t["pid"], active_loan[0]["pid"]

    login_user_via_session(client, user=User.query.get(users["admin"].id))
    item_pid, loan_pid = get_active_loan_pid_and_item_pid()
    item = Item.get_record_by_pid(item_pid)
    with pytest.raises(ItemHasActiveLoanError):
        item.commit()
Example #16
0
def testdata(app, db, es_clear):
    """Create, index and return test data."""
    indexer = RecordIndexer()

    locations = load_json_from_datadir("locations.json")
    for location in locations:
        record = Location.create(location)
        mint_record_pid(LOCATION_PID_TYPE, "pid", record)
        record.commit()
        db.session.commit()
        indexer.index(record)

    internal_locations = load_json_from_datadir("internal_locations.json")
    for internal_location in internal_locations:
        record = InternalLocation.create(internal_location)
        mint_record_pid(INTERNAL_LOCATION_PID_TYPE, "pid", record)
        record.commit()
        db.session.commit()
        indexer.index(record)

    keywords = load_json_from_datadir("keywords.json")
    for keyword in keywords:
        record = Keyword.create(keyword)
        mint_record_pid(KEYWORD_PID_TYPE, "pid", record)
        record.commit()
        db.session.commit()
        indexer.index(record)

    series_data = load_json_from_datadir("series.json")
    for series in series_data:
        record = Series.create(series)
        mint_record_pid(SERIES_PID_TYPE, "pid", record)
        record.commit()
        db.session.commit()
        indexer.index(record)

    documents = load_json_from_datadir("documents.json")
    for doc in documents:
        record = Document.create(doc)
        mint_record_pid(DOCUMENT_PID_TYPE, "pid", record)
        record.commit()
        db.session.commit()
        indexer.index(record)

    items = load_json_from_datadir("items.json")
    for item in items:
        record = Item.create(item)
        mint_record_pid(ITEM_PID_TYPE, "pid", record)
        record.commit()
        db.session.commit()
        indexer.index(record)

    eitems = load_json_from_datadir("eitems.json")
    for eitem in eitems:
        record = EItem.create(eitem)
        mint_record_pid(EITEM_PID_TYPE, "pid", record)
        record.commit()
        db.session.commit()
        indexer.index(record)

    loans = load_json_from_datadir("loans.json")
    for loan in loans:
        record = Loan.create(loan)
        mint_record_pid(CIRCULATION_LOAN_PID_TYPE, "pid", record)
        record.commit()
        db.session.commit()
        indexer.index(record)

    # flush all indices after indexing, otherwise ES won't be ready for tests
    current_search.flush_and_refresh(index='*')

    return {
        "locations": locations,
        "internal_locations": internal_locations,
        "documents": documents,
        "items": items,
        "loans": loans,
        "keywords": keywords,
        "series": series_data,
    }
Example #17
0
def index_item_after_loan_indexed(item_pid):
    """Index item to re-compute circulation reference."""
    if item_pid:
        item = Item.get_record_by_pid(item_pid)
        RecordIndexer().index(item)