diff --git a/loan-custom/caosdb-server/caosdb-webui/build.properties.d/51_box_loan.properties b/loan-custom/caosdb-server/caosdb-webui/build.properties.d/51_box_loan.properties index 09b7cf8881f5723ad9901d0d5ca5badc271e0382..f133e17dcef9a5c7c315590b621d08ae0fc992ea 100644 --- a/loan-custom/caosdb-server/caosdb-webui/build.properties.d/51_box_loan.properties +++ b/loan-custom/caosdb-server/caosdb-webui/build.properties.d/51_box_loan.properties @@ -1 +1,5 @@ BUILD_MODULE_BOX_LOAN=ENABLED +MODULE_DEPENDENCIES+=( + box_loan_config.js + box_loan.js +) diff --git a/loan-custom/caosdb-server/caosdb-webui/src/ext/html/forms/loan-forms/borrow_all_bookmarked.html b/loan-custom/caosdb-server/caosdb-webui/src/ext/html/forms/loan-forms/borrow_all_bookmarked.html new file mode 100644 index 0000000000000000000000000000000000000000..13371f427649a9ab482cbe0f048e53aea149f915 --- /dev/null +++ b/loan-custom/caosdb-server/caosdb-webui/src/ext/html/forms/loan-forms/borrow_all_bookmarked.html @@ -0,0 +1,97 @@ +<!DOCTYPE html> +<html> + +<body> + <div id="caosdb-form"> + <div class="row" id="checkout-form-{boxid}" style="margin:0px"> + <div id="caosdb-f-borrow-all-form-modal" class="modal fade" aria-labelledby="borrow-all-modal" aria-hidden="true"> + <div class="modal-dialog" style="max-width:900px" > + <div class="modal-content"> + <div class="modal-header"> + <h4 class="modal-title">Borrow all bookmaked items</h4> + </div> + <div class="modal-body "> + <form class="borrow-all-form" autocomplete="on"> + <div> + Please enter your full name and your email address. If you are doing this for the first time, a + new user record will be created. + </div> + <div class="row caosdb-form-row"> + <div class="col-sm-4"> + <div class="form-group"> + <label>First Name: + <input name="first-name" type="name" class="form-control" id="first-name" + placeholder="Enter name" required="required" value="{first_name}" /> + </label> + </div> + </div> + <div class="col-sm-4"> + <div class="form-group"> + <label>Last Name: + <input name="last-name" type="name" class="form-control" id="last-name" placeholder="Enter name" + required="required" value="{last_name}" /> + </label> + </div> + </div> + <div class="col-sm-4"> + <div class="form-group"> + <label>Email: + <input name="email" type="email" class="form-control" id="email" placeholder="Enter email" + required="required" value="{email}" /> + </label> + </div> + </div> + </div> + <div class="row caosdb-form-row"> + <div class="col-sm-4"> + <div class="form-group"> + <label>Expected Return Date: + <input name="expectedreturn" min="{mindate}" type="date" class="form-control" + id="expected-return" required="required" value="{expected_return_date}" /> + </label> + </div> + </div> + <div class="col-sm-4"> + <div class="form-group"> + <label>Destination: + <input id="loan-destination" required="required" /> + </label> + </div> + </div> + </div> + <div class="row caosdb-form-row"> + <div class="col-sm-12"> + <div class="form-group"> + <label for="loan-comment" style="margin-bottom:0px">Purpose: + <textarea id="loan-comment" class="form-control" rows="4" style="width:100%" + placeholder="Please indicate the purpose of the loan.">{purpose}</textarea> + </label> + </div> + </div> + </div> + <div class="row caosdb-form-row"> + <div class="col-sm-12"> + <div class="form-check"> + <label class="form-check-label">Are you going to + completely exhaust the contents of at lease one + {lentType}? + <input type="checkbox" class="form-check-input" id="exhaust-contents" value="no" /> + </label> + </div> + </div> + </div> + </form> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> + <button type="submit" class="btn btn-primary" form="borrow-all-form">Submit</button> + </div> + </div> + </div> + + </div> + </div> + </div> +</body> + +</html> diff --git a/loan-custom/caosdb-server/caosdb-webui/src/ext/html/forms/loan-forms/borrow_checkout.html b/loan-custom/caosdb-server/caosdb-webui/src/ext/html/forms/loan-forms/borrow_checkout.html index f183fb0ca89dffddc57bab612d52465eac2896b8..f688982fabfb4187f9a82fb2204a159f7d808915 100644 --- a/loan-custom/caosdb-server/caosdb-webui/src/ext/html/forms/loan-forms/borrow_checkout.html +++ b/loan-custom/caosdb-server/caosdb-webui/src/ext/html/forms/loan-forms/borrow_checkout.html @@ -61,7 +61,8 @@ <div class="row caosdb-form-row"> <div class="col-sm-12"> <div class="form-check"> - <label class="form-check-label">Are you going to completely exhaust the contents of this box? + <label class="form-check-label">Are you going to completely + exhaust the contents of this {lentType}? <input type="checkbox" class="form-check-input" id="exhaust-contents" value="no" /> </label> </div> diff --git a/loan-custom/caosdb-server/caosdb-webui/src/ext/js/box_loan.js b/loan-custom/caosdb-server/caosdb-webui/src/ext/js/box_loan.js index f48b25e347ad05091ec3338f127b45f1fe3a20c0..e3ba18f333436f6be8f7a899cb952519fa552393 100644 --- a/loan-custom/caosdb-server/caosdb-webui/src/ext/js/box_loan.js +++ b/loan-custom/caosdb-server/caosdb-webui/src/ext/js/box_loan.js @@ -48,7 +48,6 @@ var getHTMLForm = async function(pageName, variables) { var box_loan = function(logger, box_loan_config) { const datamodel = box_loan_config.datamodel; - const _dismiss_button = '<button class="btn btn-secondary box-loan-btn">OK</button>' const _server_did_not_respond = "The server did not respond. Please reload the page."; const _return_box_button = `<a title="Return ${datamodel.box}." class="btn btn-link box-loan-btn">Return ${datamodel.box}</a>`; @@ -143,8 +142,8 @@ var box_loan = function(logger, box_loan_config) { * Query for a Loan entity which references the box and which has no * `returned` property. */ - var get_active_loans = async function(box) { - return await query(`FIND ${datamodel.loan} WITH ${datamodel.box} -> ${box.id} AND WHICH DOES NOT HAVE A ${datamodel.returned}`); + var get_active_loans = async function(boxid) { + return await query(`FIND ${datamodel.loan} WITH ${datamodel.box} -> ${boxid} AND WHICH DOES NOT HAVE A ${datamodel.returned}`); } /** @@ -155,7 +154,7 @@ var box_loan = function(logger, box_loan_config) { loan: undefined, state: undefined, }; - const loan = (await get_active_loans(box))[0]; + const loan = (await get_active_loans(box.id))[0]; if (typeof loan === "undefined") { // no loan found return loan_state; @@ -272,7 +271,10 @@ var box_loan = function(logger, box_loan_config) { } var _add_form = async function(box, form_generator, submit_callback) { - const form = $(await form_generator(box)); + const form = $(await form_generator(getEntityID(box))); + if (typeof form === "undefined") { + return; + } form.insertAfter($(box).find('.caosdb-entity-actions-panel')).hide(); form.submit(() => { submit_callback(form[0], box); @@ -309,7 +311,10 @@ var box_loan = function(logger, box_loan_config) { */ var add_return_button = async function(box) { const but = $(_return_box_button); - + const form = await _add_form(box, generate_return_form, return_function); + if (typeof form === "undefined") { + return; + } get_actions_panel(box).append(but); const config = { @@ -321,7 +326,7 @@ var box_loan = function(logger, box_loan_config) { make_desc: getEntityName, } const dd = form_elements.make_form_field(config); - const form = await _add_form(box, generate_return_form, return_function); + $(dd).find(".col-sm-9").removeClass("col-sm-9"); $(dd).find(".col-sm-3").removeClass("col-sm-3"); $(form).find("#current-location").replaceWith(dd); @@ -370,19 +375,10 @@ var box_loan = function(logger, box_loan_config) { const but = $(_borrow_box_button); get_actions_panel(box).append(but); - const config = { - type: "reference_drop_down", - name: "destination", - label: "", - required: true, - query: `FIND Record ${datamodel.location}`, - make_desc: getEntityName, - } - const dd = form_elements.make_form_field(config); const form = await _add_form(box, generate_form_borrow_checkout, borrow_function); - $(dd).find(".col-sm-9").removeClass("col-sm-9"); - $(dd).find(".col-sm-3").removeClass("col-sm-3"); - $(form).find("#loan-destination").replaceWith(dd); + + replace_loan_destination(form); + $(form).find("label").css("display", "block"); _init_validator(form); @@ -420,7 +416,7 @@ var box_loan = function(logger, box_loan_config) { const actions_panel = get_actions_panel(box); $(actions_panel).append(wait).find('btn').hide() - const loan = (await get_active_loans(box))[0]; + const loan = (await get_active_loans(box.id))[0]; const accept_loan_request = { loan: getEntityID(loan), @@ -438,7 +434,7 @@ var box_loan = function(logger, box_loan_config) { const actions_panel = get_actions_panel(box); $(actions_panel).append(wait).find('btn').hide() - const loan = (await get_active_loans(box))[0]; + const loan = (await get_active_loans(box.id))[0]; const manual_return_request = { loan: getEntityID(loan), @@ -488,7 +484,7 @@ var box_loan = function(logger, box_loan_config) { const actions_panel = get_actions_panel(box); $(actions_panel).append(wait).find('btn').hide() - const loan = (await get_active_loans(box))[0]; + const loan = (await get_active_loans(box.id))[0]; const confirm_loan_request = { loan: getEntityID(loan), @@ -545,7 +541,7 @@ var box_loan = function(logger, box_loan_config) { const actions_panel = get_actions_panel(box); $(actions_panel).append(wait).find('btn').hide() - const loan = (await get_active_loans(box))[0]; + const loan = (await get_active_loans(box.id))[0]; const accept_return_request = { loan: getEntityID(loan), @@ -564,7 +560,7 @@ var box_loan = function(logger, box_loan_config) { const actions_panel = get_actions_panel(box); $(actions_panel).append(wait).find('btn').hide() - const loan = (await get_active_loans(box))[0]; + const loan = (await get_active_loans(box.id))[0]; const reject_return_request = { loan: getEntityID(loan), @@ -635,7 +631,7 @@ var box_loan = function(logger, box_loan_config) { const return_request = get_request_data(form); - const loan = (await get_active_loans(box))[0]; + const loan = (await get_active_loans(box.id))[0]; $.extend(return_request, { @@ -649,6 +645,53 @@ var box_loan = function(logger, box_loan_config) { show_result(actions_panel, result, box); } + var replace_loan_destination = async function(node) { + const config = { + type: "reference_drop_down", + name: "destination", + label: "", + required: true, + query: `FIND Record ${datamodel.location}`, + make_desc: getEntityName, + } + const dd = form_elements.make_form_field(config); + $(dd).find(".col-sm-9").removeClass("col-sm-9"); + $(dd).find(".col-sm-3").removeClass("col-sm-3"); + $(node).find("#loan-destination").replaceWith(dd); + } + + var add_menu_entry = async function () { + + if ($("#caosdb-f-bookmarks-borrow-all").length > 0) { + return; + } + + var modal = $(await generate_form_borrow_checkout(undefined, "loan-forms/borrow_all_bookmarked")).find(".modal"); + + replace_loan_destination(modal); + $(modal).find("label").css("display", "block"); + + $('body').append(modal); + modal.on('show.bs.modal', function() { + modal.find(".modal-footer").show(); + }); + + + modal.find('.btn-primary').on('click', async function() { + var loan_request = get_request_data(modal.find("form")[0]); + // TODO this is a dependency that cannot be expressed by adding the module to the call of this module + // because the ext_bookmarks module is initialized too late + loan_request["box"] = ext_bookmarks.get_bookmarks(); + const result = await run_script("loan_management/request_loan.py", loan_request); + show_result(modal.find(".modal-body"), result, {}); + modal.find(".modal-footer").hide(); + modal.find(".box-loan-btn").attr("data-bs-dismiss", "modal") + }); + + _init_validator(modal[0]); + const borrow_btn = $('<li class="disabled" id="caosdb-f-bookmarks-borrow-all" data-bs-toggle="modal" data-bs-target="#caosdb-f-borrow-all-form-modal" title="Borrow all bookmarked items"> <a class="dropdown-item">Borrow all</a></li>'); + $("#caosdb-f-bookmarks-clear").parent().append(borrow_btn); + } /** * Add buttons for borrowing boxes. @@ -680,7 +723,7 @@ var box_loan = function(logger, box_loan_config) { * selectform can be used to generate the form for multiple boxes. * In case selectform is true, box will be ignored. */ - var generate_form_borrow_checkout = async function(box) { + var generate_form_borrow_checkout = async function(boxid, formhtml) { const email = window.localStorage["borrower_email"]; const firstname = window.localStorage["borrower_first_name"]; const lastname = window.localStorage["borrower_last_name"]; @@ -689,16 +732,16 @@ var box_loan = function(logger, box_loan_config) { if (exp_return < Date.now()) { exp_return = ""; } - - - return getHTMLForm("loan-forms/borrow_checkout", { - boxid: getEntityID(box), + formhtml = formhtml || 'loan-forms/borrow_checkout'; + return getHTMLForm(formhtml, { + boxid: boxid, first_name: firstname, last_name: lastname, email: email, destination: destination, expected_return_date: exp_return, - mindate: get_formatted_date("T") + mindate: get_formatted_date("T"), + lentType: datamodel.box }); } @@ -714,10 +757,19 @@ var box_loan = function(logger, box_loan_config) { * currentlocation (pre-filled) * comment */ - var generate_return_form = async function(box) { - const loan = (await get_active_loans(box))[0]; + var generate_return_form = async function(boxid) { + const loan = (await get_active_loans(boxid))[0]; + if (typeof loan === "undefined") { + // no loan found + logger.error("No loan found for box", boxid); + return ; + } const borrower = (await retrieve(getProperty(loan, "Borrower", case_sensitive = false)))[0]; - + if (typeof borrower === "undefined") { + // no loan found + logger.error("No borrower found for loan", loan); + return ; + } var email = getProperty(borrower, datamodel.email, case_sensitive = false); var first_name = getProperty(borrower, datamodel.firstName, case_sensitive = false); @@ -728,7 +780,7 @@ var box_loan = function(logger, box_loan_config) { var cur_loc = getProperty(loan, "destination", case_sensitive = false); return getHTMLForm("loan-forms/return_box", { - boxid: getEntityID(box), + boxid: boxid, first_name: first_name, last_name: last_name, email: email, @@ -745,6 +797,7 @@ var box_loan = function(logger, box_loan_config) { return $(this).find(".caosdb-parent-name").text().trim() === datamodel.box; }) .toArray(); + add_menu_entry(); add_buttons(init_boxes); } diff --git a/loan-custom/caosdb-server/caosdb-webui/src/ext/js/a_box_loan_config.js b/loan-custom/caosdb-server/caosdb-webui/src/ext/js/box_loan_config.js similarity index 100% rename from loan-custom/caosdb-server/caosdb-webui/src/ext/js/a_box_loan_config.js rename to loan-custom/caosdb-server/caosdb-webui/src/ext/js/box_loan_config.js diff --git a/loanpy/src/loan/request_loan.py b/loanpy/src/loan/request_loan.py index e582089bec41c772e5d564b59e032108c87cb5bf..8a98589f55e5d11e8cbc7fad0448583f30013802 100755 --- a/loanpy/src/loan/request_loan.py +++ b/loanpy/src/loan/request_loan.py @@ -23,6 +23,7 @@ Creates a loan request using information provided by a web formular. from __future__ import absolute_import import linkahead as db +import logging from caosadvancedtools.serverside.helper import get_timestamp, print_success from linkahead.common.models import get_id_from_versionid, value_matches_versionid @@ -32,6 +33,8 @@ from .box_loan import (BORROWER, BOX, COMMENT, DESTINATION, EXHAUST_CONTENTS, EX LOAN, LOAN_REQUESTED, assert_date_in_future, assert_key_in_data, insert_or_update_person, main, send_loan_request_mail) +LOGGER_NAME = "box_loan" +LOGGER = logging.getLogger(LOGGER_NAME) def create_loan(box, borrower, expected_return, exhaust_contents, comment, destination): """ Create a new loan record. """ @@ -92,10 +95,29 @@ def _check_data(data): data[F_EXPECTED_RETURN_DATE])) +def _check_items(items): + """ Check whether the items have the correct RecordTypes """ + if not isinstance(items, list): + items = [items] + bad_type_ids = [] + for item in items: + if db.execute_query(f"COUNT {BOX.name} WITH ID='{item}'") != 1: + bad_type_ids.append(item) + + if len(bad_type_ids)>0: + LOGGER.error(f"The entities with the following IDs do not have the appropriate RecordType " + f"in order to create a loan:<br>{', '.join(bad_type_ids)}<br>") + return False + return True + + + def _issue_loan_request(data): """ Insert a loan record a insert/update a person record. """ data = _set_defaults(data) _check_data(data) + if not _check_items(data[F_BOX]): + return None, None borrower = insert_or_update_person(firstname=data[F_FIRST_NAME], lastname=data[F_LAST_NAME], email=data[F_EMAIL]) @@ -126,6 +148,8 @@ def issue_loan_request(data): identified by either email, oder firstName+lastName. """ borrower, loan = _issue_loan_request(data) + if borrower is None: + return 1 fn = borrower.get_property(FIRST_NAME.name).value ln = borrower.get_property(LAST_NAME.name).value