2549 lines
110 KiB
JavaScript
2549 lines
110 KiB
JavaScript
function display_pickup_location(state) {
|
|
var $text;
|
|
if (state.needs_override === true) {
|
|
$text = $(
|
|
"<span>" +
|
|
state.text +
|
|
'</span> <span style="float:right;" title="' +
|
|
__(
|
|
"This pickup location is not allowed according to circulation rules"
|
|
) +
|
|
'"><i class="fa fa-exclamation-circle" aria-hidden="true"></i></span>'
|
|
);
|
|
} else {
|
|
$text = $("<span>" + state.text + "</span>");
|
|
}
|
|
|
|
return $text;
|
|
}
|
|
|
|
(function ($) {
|
|
/**
|
|
* Generate a Select2 dropdown for pickup locations
|
|
*
|
|
* It expects the select object to contain several data-* attributes
|
|
* - data-pickup-location-source: 'biblio', 'item' or 'hold' (default)
|
|
* - data-patron-id: required for 'biblio' and 'item'
|
|
* - data-biblio-id: required for 'biblio' only
|
|
* - data-item-id: required for 'item' only
|
|
*
|
|
* @return {Object} The Select2 instance
|
|
*/
|
|
|
|
$.fn.pickup_locations_dropdown = function () {
|
|
var select = $(this);
|
|
var pickup_location_source = $(this).data("pickup-location-source");
|
|
var patron_id = $(this).data("patron-id");
|
|
var biblio_id = $(this).data("biblio-id");
|
|
var item_id = $(this).data("item-id");
|
|
var hold_id = $(this).data("hold-id");
|
|
|
|
var url;
|
|
|
|
if (pickup_location_source === "biblio") {
|
|
url =
|
|
"/api/v1/biblios/" +
|
|
encodeURIComponent(biblio_id) +
|
|
"/pickup_locations";
|
|
} else if (pickup_location_source === "item") {
|
|
url =
|
|
"/api/v1/items/" +
|
|
encodeURIComponent(item_id) +
|
|
"/pickup_locations";
|
|
} else {
|
|
// hold
|
|
url =
|
|
"/api/v1/holds/" +
|
|
encodeURIComponent(hold_id) +
|
|
"/pickup_locations";
|
|
}
|
|
|
|
select.kohaSelect({
|
|
width: "style",
|
|
allowClear: false,
|
|
ajax: {
|
|
url: url,
|
|
delay: 300, // wait 300 milliseconds before triggering the request
|
|
cache: true,
|
|
dataType: "json",
|
|
data: function (params) {
|
|
var search_term =
|
|
params.term === undefined ? "" : params.term;
|
|
var query = {
|
|
q: JSON.stringify({
|
|
name: { "-like": "%" + search_term + "%" },
|
|
}),
|
|
_order_by: "name",
|
|
_page: params.page,
|
|
};
|
|
|
|
if (pickup_location_source !== "hold") {
|
|
query["patron_id"] = patron_id;
|
|
}
|
|
|
|
return query;
|
|
},
|
|
processResults: function (data) {
|
|
var results = [];
|
|
data.results.forEach(function (pickup_location) {
|
|
results.push({
|
|
id: pickup_location.library_id.escapeHtml(),
|
|
text: pickup_location.name.escapeHtml(),
|
|
needs_override: pickup_location.needs_override,
|
|
});
|
|
});
|
|
return {
|
|
results: results,
|
|
pagination: { more: data.pagination.more },
|
|
};
|
|
},
|
|
},
|
|
templateResult: display_pickup_location,
|
|
});
|
|
|
|
return select;
|
|
};
|
|
})(jQuery);
|
|
|
|
/* global __ borrowernumber SuspendHoldsIntranet */
|
|
$(document).ready(function () {
|
|
window.coded_values = {
|
|
library: new Map(all_libraries.map(l => [l.branchname, l.branchcode])),
|
|
};
|
|
|
|
let patron_page = holds_table_patron_page();
|
|
function suspend_hold(hold_ids, end_date) {
|
|
var params = { hold_ids: hold_ids };
|
|
if (end_date !== null && end_date !== "") params.end_date = end_date;
|
|
|
|
return $.ajax({
|
|
method: "POST",
|
|
url: "/api/v1/holds/suspension_bulk",
|
|
contentType: "application/json",
|
|
data: JSON.stringify(params),
|
|
});
|
|
}
|
|
|
|
function resume_hold(hold_id) {
|
|
return $.ajax({
|
|
method: "DELETE",
|
|
url: "/api/v1/holds/" + encodeURIComponent(hold_id) + "/suspension",
|
|
}).done(function () {
|
|
if ($(".select_hold_all").prop("checked")) {
|
|
$(".select_hold_all").click();
|
|
}
|
|
});
|
|
}
|
|
|
|
var holdsTable;
|
|
|
|
// Don't load holds table unless it is clicked on
|
|
$("#holds-tab").on("click", function () {
|
|
load_holds_table();
|
|
});
|
|
|
|
// If the holds tab is preselected on load, we need to load the table
|
|
if ($("#holds-tab").hasClass("active")) {
|
|
load_holds_table();
|
|
}
|
|
|
|
function load_holds_table() {
|
|
var holds = new Array();
|
|
if (!holdsTable) {
|
|
var title;
|
|
|
|
holdsTable = $("#holds-table").kohaTable(
|
|
{
|
|
autoWidth: false,
|
|
dom: '<"table_controls"B>rt',
|
|
columns: [
|
|
{
|
|
orderable: false,
|
|
data: function (oObj) {
|
|
return (
|
|
'<input type="checkbox" class="select_hold" data-id="' +
|
|
oObj.reserve_id +
|
|
(oObj.hold_group_id
|
|
? '" data-hold-group-id="' +
|
|
oObj.hold_group_id +
|
|
'"'
|
|
: '"') +
|
|
'" data-borrowernumber="' +
|
|
borrowernumber +
|
|
'" data-biblionumber="' +
|
|
oObj.biblionumber +
|
|
'">'
|
|
);
|
|
},
|
|
},
|
|
{
|
|
data: {
|
|
_: "reservedate_formatted",
|
|
sort: "reservedate",
|
|
},
|
|
},
|
|
...(DisplayAddHoldGroups
|
|
? [
|
|
{
|
|
data: function (oObj) {
|
|
title = "";
|
|
if (oObj.hold_group_id) {
|
|
var link =
|
|
'<a class="hold-group" href="/cgi-bin/koha/reserve/hold-group.pl?hold_group_id=' +
|
|
oObj.hold_group_id +
|
|
'">' +
|
|
oObj.hold_group_id +
|
|
"</a>";
|
|
|
|
title =
|
|
"<span>" + link + "</span>";
|
|
}
|
|
|
|
return title;
|
|
},
|
|
},
|
|
]
|
|
: []),
|
|
{
|
|
data: function (oObj) {
|
|
title =
|
|
"<a href='/cgi-bin/koha/reserve/request.pl?biblionumber=" +
|
|
oObj.biblionumber +
|
|
"'>" +
|
|
(oObj.title ? oObj.title.escapeHtml() : "");
|
|
|
|
$.each(oObj.subtitle, function (index, value) {
|
|
title += " " + value.escapeHtml();
|
|
});
|
|
|
|
title +=
|
|
" " +
|
|
oObj.part_number +
|
|
" " +
|
|
oObj.part_name;
|
|
|
|
if (oObj.enumchron) {
|
|
title +=
|
|
" (" +
|
|
oObj.enumchron.escapeHtml() +
|
|
")";
|
|
}
|
|
|
|
title += "</a>";
|
|
|
|
if (oObj.author) {
|
|
title +=
|
|
" " +
|
|
__("by _AUTHOR_").replace(
|
|
"_AUTHOR_",
|
|
oObj.author.escapeHtml()
|
|
);
|
|
}
|
|
|
|
if (oObj.itemnotes) {
|
|
var span_class = "";
|
|
if (
|
|
flatpickr.formatDate(
|
|
new Date(oObj.issuedate),
|
|
"Y-m-d"
|
|
) == ymd
|
|
) {
|
|
span_class = "circ-hlt";
|
|
}
|
|
title +=
|
|
" - <span class='" +
|
|
span_class +
|
|
"'>" +
|
|
oObj.itemnotes.escapeHtml() +
|
|
"</span>";
|
|
}
|
|
|
|
if (oObj.is_hold_group_target) {
|
|
var link = __("target of hold group");
|
|
title +=
|
|
'<br><span class="fw-bold fst-italic">(' +
|
|
link +
|
|
")</span>";
|
|
}
|
|
|
|
if (oObj.iso18626_request) {
|
|
title += create_iso18626_request_link(
|
|
oObj.iso18626_request.id,
|
|
oObj.iso18626_request.status
|
|
);
|
|
}
|
|
|
|
return title;
|
|
},
|
|
},
|
|
{
|
|
data: function (oObj) {
|
|
return (
|
|
(oObj.itemcallnumber &&
|
|
oObj.itemcallnumber.escapeHtml()) ||
|
|
""
|
|
);
|
|
},
|
|
},
|
|
{
|
|
data: function (oObj) {
|
|
var data = "";
|
|
if (oObj.itemtype) {
|
|
data += oObj.itemtype_description;
|
|
}
|
|
return data;
|
|
},
|
|
},
|
|
{
|
|
data: function (oObj) {
|
|
var data = "";
|
|
if (oObj.barcode) {
|
|
data +=
|
|
" <a href='/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=" +
|
|
oObj.biblionumber +
|
|
"&itemnumber=" +
|
|
oObj.itemnumber +
|
|
"#item" +
|
|
oObj.itemnumber +
|
|
"'>" +
|
|
oObj.barcode.escapeHtml() +
|
|
"</a>";
|
|
}
|
|
return data;
|
|
},
|
|
},
|
|
{
|
|
data: function (oObj) {
|
|
if (
|
|
oObj.branches.length > 1 &&
|
|
oObj.found !== "W" &&
|
|
oObj.found !== "T"
|
|
) {
|
|
var branchSelect =
|
|
"<select priority=" +
|
|
oObj.priority +
|
|
' class="hold_location_select" data-hold-id="' +
|
|
oObj.reserve_id +
|
|
'" reserve_id="' +
|
|
oObj.reserve_id +
|
|
'" name="pick-location" data-pickup-location-source="hold">';
|
|
for (
|
|
var i = 0;
|
|
i < oObj.branches.length;
|
|
i++
|
|
) {
|
|
var selectedbranch;
|
|
var setbranch;
|
|
if (oObj.branches[i].selected) {
|
|
selectedbranch =
|
|
" selected='selected' ";
|
|
setbranch = __(" (current) ");
|
|
} else if (
|
|
oObj.branches[i].pickup_location ==
|
|
0
|
|
) {
|
|
continue;
|
|
} else {
|
|
selectedbranch = "";
|
|
setbranch = "";
|
|
}
|
|
branchSelect +=
|
|
'<option value="' +
|
|
oObj.branches[
|
|
i
|
|
].branchcode.escapeHtml() +
|
|
'"' +
|
|
selectedbranch +
|
|
">" +
|
|
oObj.branches[
|
|
i
|
|
].branchname.escapeHtml() +
|
|
setbranch +
|
|
"</option>";
|
|
}
|
|
branchSelect += "</select>";
|
|
return branchSelect;
|
|
} else {
|
|
return oObj.branchcode.escapeHtml() || "";
|
|
}
|
|
},
|
|
},
|
|
{
|
|
data: {
|
|
_: "expirationdate_formatted",
|
|
sort: "expirationdate",
|
|
},
|
|
},
|
|
{
|
|
data: function (oObj) {
|
|
if (
|
|
oObj.priority &&
|
|
parseInt(oObj.priority) &&
|
|
parseInt(oObj.priority) > 0
|
|
) {
|
|
return oObj.priority;
|
|
} else {
|
|
return "";
|
|
}
|
|
},
|
|
},
|
|
{
|
|
data: function (oObj) {
|
|
return (
|
|
(oObj.reservenotes &&
|
|
oObj.reservenotes.escapeHtml()) ||
|
|
""
|
|
);
|
|
},
|
|
},
|
|
{
|
|
data: function (oObj) {
|
|
return (
|
|
'<a class="cancel-hold deny" title="Cancel hold" data-borrowernumber="' +
|
|
borrowernumber +
|
|
'" data-biblionumber="' +
|
|
oObj.biblionumber +
|
|
'" data-id="' +
|
|
oObj.reserve_id +
|
|
'" href="#"> <i class="fa fa-trash" aria-label="Cancel hold"></i></a>'
|
|
);
|
|
},
|
|
},
|
|
{
|
|
orderable: false,
|
|
visible: SuspendHoldsIntranet,
|
|
data: function (oObj) {
|
|
holds[oObj.reserve_id] = oObj; //Store holds for later use
|
|
|
|
if (!SuspendHoldsIntranet) {
|
|
return "";
|
|
}
|
|
|
|
if (oObj.found) {
|
|
return "";
|
|
} else if (oObj.suspend == 1) {
|
|
return (
|
|
"<a class='hold-resume btn btn-default btn-xs' data-hold-id='" +
|
|
oObj.reserve_id +
|
|
"'>" +
|
|
"<i class='fa fa-play'></i> " +
|
|
__("Resume") +
|
|
"</a>"
|
|
);
|
|
} else {
|
|
const link = Object.assign(
|
|
document.createElement("a"),
|
|
{
|
|
className:
|
|
"hold-suspend btn btn-default btn-xs",
|
|
textContent: " " + __("Suspend"),
|
|
}
|
|
);
|
|
link.setAttribute(
|
|
"data-hold-id",
|
|
oObj.reserve_id
|
|
);
|
|
link.setAttribute(
|
|
"data-hold-title",
|
|
oObj.title
|
|
);
|
|
const icon = Object.assign(
|
|
document.createElement("i"),
|
|
{
|
|
className: "fa fa-pause",
|
|
}
|
|
);
|
|
link.prepend(icon);
|
|
return link.outerHTML;
|
|
}
|
|
},
|
|
},
|
|
{
|
|
data: function (oObj) {
|
|
var data = "";
|
|
|
|
if (oObj.suspend == 1) {
|
|
data +=
|
|
"<p>" +
|
|
__(
|
|
"Hold is <strong>suspended</strong>"
|
|
);
|
|
if (oObj.suspend_until) {
|
|
data +=
|
|
" " +
|
|
__("until %s").format(
|
|
oObj.suspend_until_formatted
|
|
);
|
|
}
|
|
data += "</p>";
|
|
}
|
|
|
|
if (oObj.itemtype_limit) {
|
|
data += __("Next available %s item").format(
|
|
oObj.itemtype_limit
|
|
);
|
|
}
|
|
|
|
if (oObj.item_group_id) {
|
|
data += __(
|
|
"Next available item group <strong>%s</strong> item"
|
|
).format(oObj.item_group_description);
|
|
}
|
|
|
|
if (oObj.barcode) {
|
|
data += "<em>";
|
|
if (oObj.found == "W") {
|
|
if (oObj.waiting_here) {
|
|
data += __(
|
|
"Item is <strong>waiting here</strong>"
|
|
);
|
|
if (oObj.desk_name) {
|
|
data +=
|
|
", " +
|
|
__("at %s").format(
|
|
oObj.desk_name.escapeHtml()
|
|
);
|
|
}
|
|
} else {
|
|
data += __(
|
|
"Item is <strong>waiting</strong>"
|
|
);
|
|
data +=
|
|
" " +
|
|
__("at %s").format(
|
|
oObj.waiting_at
|
|
);
|
|
if (oObj.desk_name) {
|
|
data +=
|
|
", " +
|
|
__("at %s").format(
|
|
oObj.desk_name.escapeHtml()
|
|
);
|
|
}
|
|
}
|
|
} else if (
|
|
oObj.found == "T" &&
|
|
oObj.transferred
|
|
) {
|
|
data += __(
|
|
"Item is <strong>in transit</strong> from %s since %s"
|
|
).format(
|
|
oObj.from_branch,
|
|
oObj.date_sent
|
|
);
|
|
} else if (
|
|
oObj.found == "T" &&
|
|
oObj.not_transferred
|
|
) {
|
|
data += __(
|
|
"Item hasn't been transferred yet from %s"
|
|
).format(oObj.not_transferred_by);
|
|
}
|
|
data += "</em>";
|
|
}
|
|
return data;
|
|
},
|
|
},
|
|
],
|
|
paging: false,
|
|
processing: true,
|
|
serverSide: false,
|
|
ajax: {
|
|
url: "/cgi-bin/koha/svc/holds",
|
|
data: function (d) {
|
|
d.borrowernumber = borrowernumber;
|
|
},
|
|
},
|
|
bKohaAjaxSVC: true,
|
|
},
|
|
table_settings_holds_table
|
|
);
|
|
|
|
$("#holds-table").on("draw.dt", function () {
|
|
$(".hold-suspend").on("click", function () {
|
|
var hold_id = $(this).data("hold-id");
|
|
var hold_title = $(this).data("hold-title");
|
|
$("#suspend-modal-title").text(hold_title);
|
|
$("#suspend-modal-submit").data("hold-id", hold_id);
|
|
$("#suspend-modal").modal("show");
|
|
});
|
|
|
|
$(".hold-resume").on("click", function () {
|
|
var hold_id = $(this).data("hold-id");
|
|
resume_hold(hold_id)
|
|
.success(function () {
|
|
holdsTable.api().ajax.reload();
|
|
})
|
|
.error(function (jqXHR, textStatus, errorThrown) {
|
|
if (jqXHR.status === 404) {
|
|
alert(__("Unable to resume, hold not found"));
|
|
} else {
|
|
alert(
|
|
__(
|
|
"Your request could not be processed. Check the logs for details."
|
|
)
|
|
);
|
|
}
|
|
holdsTable.api().ajax.reload();
|
|
});
|
|
});
|
|
|
|
$(".hold_location_select").each(function () {
|
|
$(this).pickup_locations_dropdown();
|
|
});
|
|
|
|
$(".hold_location_select").on("change", function () {
|
|
$(this).prop("disabled", true);
|
|
var cur_select = $(this);
|
|
var res_id = $(this).attr("reserve_id");
|
|
$(this).after(
|
|
'<div id="updating_reserveno' +
|
|
res_id +
|
|
'" class="waiting"><img src="/intranet-tmpl/prog/img/spinner-small.gif" alt="" /><span class="waiting_msg"></span></div>'
|
|
);
|
|
var api_url =
|
|
"/api/v1/holds/" +
|
|
encodeURIComponent(res_id) +
|
|
"/pickup_location";
|
|
$.ajax({
|
|
method: "PUT",
|
|
url: api_url,
|
|
data: JSON.stringify({
|
|
pickup_library_id: $(this).val(),
|
|
}),
|
|
headers: { "x-koha-override": "any" },
|
|
success: function (data) {
|
|
holdsTable.api().ajax.reload();
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
alert(
|
|
"There was an error:" +
|
|
textStatus +
|
|
" " +
|
|
errorThrown
|
|
);
|
|
cur_select.prop("disabled", false);
|
|
$("#updating_reserveno" + res_id).remove();
|
|
cur_select.val(
|
|
cur_select
|
|
.children('option[selected="selected"]')
|
|
.val()
|
|
);
|
|
},
|
|
});
|
|
});
|
|
});
|
|
|
|
if ($("#holds-table").length) {
|
|
$("#holds-table_processing").position({
|
|
of: $("#holds-table"),
|
|
collision: "none",
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
$("#suspend-modal-clear-date").on("click", function () {
|
|
$("#suspend-modal-until").flatpickr().clear();
|
|
});
|
|
|
|
$("#suspend-modal-submit").on("click", function (e) {
|
|
e.preventDefault();
|
|
let selected_holds;
|
|
if (!$(this).data("hold-id")) {
|
|
selected_holds = get_selected_holds_data();
|
|
} else {
|
|
selected_holds =
|
|
"[" + JSON.stringify({ hold: $(this).data("hold-id") }) + "]";
|
|
$(this).removeData("hold-id");
|
|
}
|
|
|
|
var suspend_until_date = $("#suspend-modal-until").val();
|
|
if (suspend_until_date !== null)
|
|
suspend_until_date = $date(suspend_until_date, {
|
|
dateformat: "rfc3339",
|
|
});
|
|
|
|
const hold_ids = JSON.parse(selected_holds).map(hold => hold.hold);
|
|
try {
|
|
suspend_hold(hold_ids, suspend_until_date)
|
|
.success(function () {
|
|
holdsTable.api().ajax.reload();
|
|
})
|
|
.done(function () {
|
|
if ($("#suspend-modal-until").length) {
|
|
$("#suspend-modal-until").flatpickr().clear(); // clean the input
|
|
}
|
|
$("#suspend-modal").modal("hide");
|
|
$(".select_hold_all").click();
|
|
});
|
|
} catch (error) {
|
|
if (error.status === 404) {
|
|
alert(__("Unable to suspend, hold not found."));
|
|
} else {
|
|
alert(
|
|
__(
|
|
"Your request could not be processed. Check the logs for details."
|
|
)
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
function toggle_suspend(node, inputs) {
|
|
let reserve_id = $(node).data("reserve-id");
|
|
let biblionumber = $(node).data("biblionumber");
|
|
let suspendForm = $("#hold-actions-form").attr({
|
|
action: "request.pl",
|
|
method: "post",
|
|
});
|
|
let sus_bn = $("<input />").attr({
|
|
type: "hidden",
|
|
name: "biblionumber",
|
|
value: biblionumber,
|
|
});
|
|
let sus_ri = $("<input />").attr({
|
|
type: "hidden",
|
|
name: "reserve_id",
|
|
value: reserve_id,
|
|
});
|
|
inputs.push(sus_bn, sus_ri);
|
|
suspendForm.append(inputs);
|
|
$("#hold-actions-form").submit();
|
|
return false;
|
|
}
|
|
$(".suspend-hold").on("click", function (e) {
|
|
e.preventDefault();
|
|
let reserve_id = $(this).data("reserve-id");
|
|
let suspend_until = $("#suspend_until_" + reserve_id).val();
|
|
let inputs = [
|
|
$("<input />").attr({
|
|
type: "hidden",
|
|
name: "op",
|
|
value: "cud-suspend",
|
|
}),
|
|
$("<input />").attr({
|
|
type: "hidden",
|
|
name: "suspend_until",
|
|
value: suspend_until,
|
|
}),
|
|
];
|
|
return toggle_suspend(this, inputs);
|
|
});
|
|
|
|
var MSG_SUSPEND_SELECTED_HOLDS = __("selected holds");
|
|
|
|
$(".suspend_selected_holds").click(function (e) {
|
|
e.preventDefault();
|
|
if (!$(".holds_table .select_hold:checked").length) {
|
|
return false;
|
|
}
|
|
$("#suspend-modal-title").html(MSG_SUSPEND_SELECTED_HOLDS);
|
|
$("#suspend-modal").modal("show");
|
|
return false;
|
|
});
|
|
|
|
$(".unsuspend-hold").on("click", function (e) {
|
|
e.preventDefault();
|
|
let inputs = [
|
|
$("<input />").attr({
|
|
type: "hidden",
|
|
name: "op",
|
|
value: "cud-unsuspend",
|
|
}),
|
|
];
|
|
return toggle_suspend(this, inputs);
|
|
});
|
|
|
|
var MSG_MOVE_SELECTED = __("Move selected (%s)");
|
|
var MSG_RECALL_SELECTED = __("Convert selected to recall (%s)");
|
|
var MSG_CANCEL_ALERT = __(
|
|
"This action will cancel <span class='badge bg-danger'>%s</span> hold(s)."
|
|
);
|
|
var MSG_CONVERT_ALERT = __(
|
|
"This action will cancel <span class='badge bg-danger'>%s</span> hold(s) and create <span class='badge bg-danger'>%s</span> recall(s)."
|
|
);
|
|
|
|
// Confirm cancellation of hold
|
|
let cancel_link;
|
|
$(document).on("click", ".cancel-hold", function (e) {
|
|
e.preventDefault();
|
|
cancel_link = $(this);
|
|
$("#cancel_modal_form #inputs").empty();
|
|
let reserve_id = cancel_link.data("id");
|
|
let biblionumber = cancel_link.data("biblionumber");
|
|
if (!patron_page) {
|
|
$("#cancel_modal_form #inputs").append(
|
|
'<input type="hidden" name="reserve_id" value="' +
|
|
reserve_id +
|
|
'">'
|
|
);
|
|
$("#cancel_modal_form #inputs").append(
|
|
'<input type="hidden" name="biblionumber" value="' +
|
|
biblionumber +
|
|
'">'
|
|
);
|
|
$("#cancel_modal_form #inputs").append(
|
|
'<input type="hidden" name="op" value="cud-cancel">'
|
|
);
|
|
} else {
|
|
_append_patron_page_cancel_hold_modal_data({
|
|
hold: reserve_id,
|
|
biblionumber: biblionumber,
|
|
borrowernumber: cancel_link.data("borrowernumber"),
|
|
});
|
|
}
|
|
$("#cancelModal").modal("show");
|
|
return false;
|
|
});
|
|
|
|
if (
|
|
!localStorage.selectedHolds ||
|
|
document.referrer.replace(/\?.*/, "") !==
|
|
document.location.origin + document.location.pathname
|
|
) {
|
|
localStorage.selectedHolds = [];
|
|
}
|
|
|
|
$(".holds_table .select_hold").each(function () {
|
|
if (localStorage.selectedHolds.includes($(this).data("id"))) {
|
|
$(this).prop("checked", true);
|
|
}
|
|
});
|
|
|
|
function updateSelectedHoldsButtonCounters() {
|
|
$(".move_selected_holds").html(
|
|
MSG_MOVE_SELECTED.format(
|
|
$(".holds_table .select_hold:checked").length
|
|
)
|
|
);
|
|
$(".selected_holds_count").html(
|
|
$(".holds_table .select_hold:checked").length
|
|
);
|
|
var selectedHolds = $(".holds_table .select_hold:checked");
|
|
var hasSelectedHolds = selectedHolds.length > 0;
|
|
var hasMultipleSelectedHolds = selectedHolds.length >= 2;
|
|
|
|
$(
|
|
".cancel_selected_holds, .suspend_selected_holds, .convert_selected_holds"
|
|
).prop("disabled", !hasSelectedHolds);
|
|
$(".group_selected_holds").prop("disabled", !hasMultipleSelectedHolds);
|
|
}
|
|
|
|
function updateMoveButtons(table) {
|
|
var checked_holds = $(".select_hold:checked", table);
|
|
var checked_count = checked_holds.length;
|
|
|
|
var item_level_count = checked_holds.filter(function () {
|
|
return $(this).attr("data-item_level_hold") !== "";
|
|
}).length;
|
|
|
|
var record_level_count = checked_holds.filter(function () {
|
|
return $(this).attr("data-item_level_hold") === "";
|
|
}).length;
|
|
|
|
$(".move_hold_item").toggleClass("disabled", item_level_count <= 0);
|
|
$(".move_hold_biblio").toggleClass("disabled", record_level_count <= 0);
|
|
$(".move_selected_holds").prop("disabled", !checked_count);
|
|
}
|
|
|
|
if (holds_table_patron_page()) {
|
|
updateSelectedHoldsButtonCounters();
|
|
|
|
$(".holds_table .select_hold_all").click(function () {
|
|
var table = $(".holds_table:not(.fixedHeader-floating)");
|
|
|
|
var checked_count = $(".select_hold:checked", table).length;
|
|
$(".select_hold", table).prop("checked", !checked_count);
|
|
$(this).prop("checked", !checked_count);
|
|
|
|
updateMoveButtons(table);
|
|
|
|
updateSelectedHoldsButtonCounters();
|
|
$("#cancel_hold_alert").html(
|
|
MSG_CANCEL_ALERT.format(
|
|
$(".holds_table .select_hold:checked").length
|
|
)
|
|
);
|
|
$("#cancel_hold_alert").show();
|
|
localStorage.selectedHolds =
|
|
"[" +
|
|
$(".holds_table .select_hold:checked")
|
|
.toArray()
|
|
.map(el =>
|
|
JSON.stringify({
|
|
hold: $(el).data("id"),
|
|
borrowernumber: $(el).data("borrowernumber"),
|
|
biblionumber: $(el).data("biblionumber"),
|
|
})
|
|
)
|
|
.join(",") +
|
|
"]";
|
|
$("#convert_hold_alert").html(
|
|
MSG_CONVERT_ALERT.format(
|
|
$(".holds_table .select_hold:checked").length,
|
|
$(".holds_table .select_hold:checked").length
|
|
)
|
|
);
|
|
$("#convert_hold_alert").show();
|
|
localStorage.selectedHolds =
|
|
"[" +
|
|
$(".holds_table .select_hold:checked")
|
|
.toArray()
|
|
.map(el =>
|
|
JSON.stringify({
|
|
hold: $(el).data("reserve_id"),
|
|
borrowernumber: $(el).data("borrowernumber"),
|
|
biblionumber: $(el).data("biblionumber"),
|
|
})
|
|
)
|
|
.join(",") +
|
|
"]";
|
|
});
|
|
|
|
$(".holds_table").on("click", ".select_hold", function () {
|
|
var table = $(this).parents(".holds_table");
|
|
var count = $(".select_hold:not(:checked)", table).length;
|
|
$(".select_hold_all", table).prop("checked", !count);
|
|
|
|
updateMoveButtons(table);
|
|
|
|
updateSelectedHoldsButtonCounters();
|
|
$("#cancel_hold_alert").html(
|
|
MSG_CANCEL_ALERT.format(
|
|
$(".holds_table .select_hold:checked").length
|
|
)
|
|
);
|
|
$("#cancel_hold_alert").show();
|
|
localStorage.selectedHolds =
|
|
"[" +
|
|
$(".holds_table .select_hold:checked")
|
|
.toArray()
|
|
.map(el =>
|
|
JSON.stringify({
|
|
hold: $(el).data("id"),
|
|
borrowernumber: $(el).data("borrowernumber"),
|
|
biblionumber: $(el).data("biblionumber"),
|
|
})
|
|
)
|
|
.join(",") +
|
|
"]";
|
|
});
|
|
|
|
$(".cancel_selected_holds").click(function (e) {
|
|
e.preventDefault();
|
|
if ($(".holds_table .select_hold:checked").length) {
|
|
$("#cancel_modal_form #inputs").empty();
|
|
$("#cancel_modal_form #inputs").append(
|
|
'<input type="hidden" name="op" value="cud-cancelall">'
|
|
);
|
|
let hold_data =
|
|
"[" +
|
|
$(".holds_table .select_hold:checked")
|
|
.toArray()
|
|
.map(el =>
|
|
JSON.stringify({
|
|
hold: $(el).data("id"),
|
|
borrowernumber: $(el).data("borrowernumber"),
|
|
biblionumber: $(el).data("biblionumber"),
|
|
})
|
|
)
|
|
.join(",") +
|
|
"]";
|
|
JSON.parse(hold_data).forEach(function (hold) {
|
|
_append_patron_page_cancel_hold_modal_data(hold);
|
|
});
|
|
delete localStorage.selectedHolds;
|
|
$("#cancelModal").modal("show");
|
|
}
|
|
return false;
|
|
});
|
|
|
|
$(".convert_selected_holds").click(function (e) {
|
|
e.preventDefault();
|
|
if ($(".holds_table .select_hold:checked").length) {
|
|
$("#convert_to_recall_modal_form #inputs").empty();
|
|
$("#convert_to_recall_modal_form #inputs").append(
|
|
'<input type="hidden" name="op" value="cud-convertall">'
|
|
);
|
|
$(
|
|
"#convert_to_recall_modal_form #modal-cancellation-reason"
|
|
).val("RECALLED");
|
|
let hold_data =
|
|
"[" +
|
|
$(".holds_table .select_hold:checked")
|
|
.toArray()
|
|
.map(el =>
|
|
JSON.stringify({
|
|
hold: $(el).data("reserve_id"),
|
|
borrowernumber: $(el).data("borrowernumber"),
|
|
biblionumber: $(el).data("biblionumber"),
|
|
})
|
|
)
|
|
.join(",") +
|
|
"]";
|
|
JSON.parse(hold_data).forEach(function (hold) {
|
|
_append_patron_page_convert_hold_modal_data(hold);
|
|
});
|
|
delete localStorage.selectedHolds;
|
|
$("#convertModal").modal("show");
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
$("#itemSearchForm").on("submit", function (event) {
|
|
event.preventDefault();
|
|
$("#move_hold_item_confirm").prop("disabled", true);
|
|
|
|
let externalID = $("#external_id").val();
|
|
let apiUrl = `/api/v1/items?external_id=${encodeURIComponent(externalID)}`;
|
|
|
|
$.ajax({
|
|
url: apiUrl,
|
|
method: "GET",
|
|
dataType: "json",
|
|
success: function (data) {
|
|
// Filter for exact matches only
|
|
let exactMatches = data.filter(
|
|
item => item.external_id === externalID
|
|
);
|
|
if (exactMatches.length > 0) {
|
|
let resultHtml = "";
|
|
$.each(exactMatches, function (index, item) {
|
|
resultHtml += `
|
|
<div class="alert alert-success">
|
|
<strong>Biblionumber:</strong> ${item.biblio_id} <br>
|
|
<strong>Item:</strong> ${item.external_id} <br>
|
|
<input id="new_itemnumber_${item.item_id}" name="new_itemnumber" type="checkbox" value="${item.item_id}">
|
|
<label for="new_itemnumber_${item.item_id}">${__("Move all selected item level holds to this item")}</label>
|
|
<input id="new_biblionumber_${item.item_id}" name="new_biblionumber" type="hidden" value="${item.biblio_id}">
|
|
</div>
|
|
<hr />
|
|
`;
|
|
});
|
|
$("#itemResultMessage").html(resultHtml);
|
|
} else {
|
|
$("#itemResultMessage").html(`
|
|
<div class="alert alert-warning">${__("No item found with barcode: %s").format(externalID)}.</div>
|
|
`);
|
|
}
|
|
},
|
|
});
|
|
});
|
|
|
|
$("#biblioSearchForm").on("submit", function (event) {
|
|
event.preventDefault();
|
|
$("#move_hold_biblio_confirm").prop("disabled", true);
|
|
|
|
let biblioID = parseInt($("#biblio_id").val());
|
|
|
|
if (Number.isNaN(biblioID)) {
|
|
$("#biblioResultMessage").html(
|
|
'<div class="alert alert-warning">' +
|
|
__("%s is not a valid biblionumber").format(
|
|
$("#biblio_id").val()
|
|
) +
|
|
"</div>"
|
|
);
|
|
return;
|
|
}
|
|
|
|
let apiUrl = `/api/v1/biblios?q={"biblio_id":"${encodeURIComponent(biblioID)}"}`;
|
|
$.ajax({
|
|
url: apiUrl,
|
|
method: "GET",
|
|
dataType: "json",
|
|
headers: {
|
|
Accept: "application/json",
|
|
},
|
|
success: function (data) {
|
|
// Filter for exact matches only
|
|
let exactMatches = data.filter(
|
|
item => item.biblio_id === biblioID
|
|
);
|
|
|
|
if (exactMatches.length > 0) {
|
|
let resultHtml = "";
|
|
$.each(exactMatches, function (index, item) {
|
|
resultHtml += `
|
|
<div class="alert alert-success">
|
|
<strong>Biblionumber:</strong> ${item.biblio_id} <br>
|
|
<input id="new_biblionumber_${item.biblio_id}" name="new_biblionumber" type="checkbox" value="${item.biblio_id}">
|
|
<label for="new_biblionumber_${item.biblio_id}">${__("Move all selected record level holds to this record")}</label>
|
|
</div>
|
|
<hr />
|
|
`;
|
|
});
|
|
$("#biblioResultMessage").html(resultHtml);
|
|
} else {
|
|
$("#biblioResultMessage").html(`
|
|
<div class="alert alert-warning">${__("No record found with biblionumber: %s").format(externalID)}.</div>
|
|
`);
|
|
}
|
|
},
|
|
});
|
|
});
|
|
|
|
$(document).on("change", 'input[name="new_itemnumber"]', function () {
|
|
$('input[name="new_itemnumber"]').not(this).prop("checked", false);
|
|
if ($('input[name="new_itemnumber"]:checked').length) {
|
|
$("#move_hold_item_confirm").prop("disabled", false);
|
|
} else {
|
|
$("#move_hold_item_confirm").prop("disabled", true);
|
|
}
|
|
});
|
|
|
|
$(document).on("change", 'input[name="new_biblionumber"]', function () {
|
|
$('input[name="new_biblionumber"]').not(this).prop("checked", false);
|
|
if ($('input[name="new_biblionumber"]:checked').length) {
|
|
$("#move_hold_biblio_confirm").prop("disabled", false);
|
|
} else {
|
|
$("#move_hold_biblio_confirm").prop("disabled", true);
|
|
}
|
|
});
|
|
|
|
$(".move_hold_item").click(function (e) {
|
|
e.preventDefault();
|
|
$("#move_hold_item_confirm").prop("disabled", true);
|
|
if ($(".holds_table .select_hold:checked").length) {
|
|
$("#itemResultMessage").empty();
|
|
$("#move_hold_item_selection table tbody").empty();
|
|
$("#moveHoldItemModal").modal("show");
|
|
const selectedHolds =
|
|
JSON.parse(localStorage.selectedHolds) ||
|
|
$(".select_hold:checked");
|
|
$(selectedHolds).each(function () {
|
|
let reserve_id = this.hold || $(this).data("id");
|
|
let reserve_biblionumber =
|
|
this.biblionumber || $(this).data("biblionumber");
|
|
let reserve_itemnumber =
|
|
this.itemnumber || $(this).data("itemnumber");
|
|
let item_level_hold =
|
|
this.item_level_hold || $(this).data("item_level_hold");
|
|
let item_waiting = this.waiting || $(this).data("waiting");
|
|
let item_intransit =
|
|
this.intransit || $(this).data("intransit");
|
|
let error_message =
|
|
this.item_level_hold || $(this).data("item_level_hold")
|
|
? ""
|
|
: __(
|
|
"Cannot move a waiting, in transit, or record level hold"
|
|
);
|
|
let found_status = $(this).data("found");
|
|
if (item_level_hold && (!item_waiting || !item_intransit)) {
|
|
$("#move_hold_item_selection table").append(
|
|
`<tr><td><input type="checkbox" name="move_hold_id" value="${reserve_id}" checked /></td><td>${reserve_id}</td><td>${__("Biblionumber:")} <a target="_blank" href="/cgi-bin/koha/reserve/request.pl?biblionumber=${reserve_biblionumber}">${reserve_biblionumber}</a> ${__("Itemnumber:")} <a target="_blank" href="/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=${reserve_biblionumber}#item${reserve_itemnumber}">${reserve_itemnumber}</a></td><td>${error_message}</td></tr>`
|
|
);
|
|
} else {
|
|
$("#move_hold_item_selection table").append(
|
|
`<tr><td><input type="checkbox" name="move_hold_id" value="${reserve_id}" disabled /></td><td>${reserve_id}</td><td>Biblionumber: <a target="_blank" href="/cgi-bin/koha/reserve/request.pl?biblionumber=${reserve_biblionumber}">${reserve_biblionumber}</a> ${__("Itemnumber:")} <a target="_blank" href="/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=${reserve_biblionumber}#item${reserve_itemnumber}">${reserve_itemnumber}</a></td><td>${error_message}</td></tr>`
|
|
);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
$(".move_hold_biblio").click(function (e) {
|
|
e.preventDefault();
|
|
$("#move_hold_biblio_confirm").prop("disabled", true);
|
|
const selectedHolds =
|
|
JSON.parse(localStorage.selectedHolds || "[]") ||
|
|
$(".select_hold:checked");
|
|
if (selectedHolds.length) {
|
|
$("#biblioResultMessage").empty();
|
|
$("#move_hold_biblio_selection table tbody").empty();
|
|
$("#moveHoldBiblioModal").modal("show");
|
|
$(selectedHolds).each(function () {
|
|
let reserve_id = this.hold || $(this).data("id");
|
|
let reserve_biblionumber =
|
|
this.biblionumber || $(this).data("biblionumber");
|
|
let reserve_itemnumber =
|
|
this.itemnumber || $(this).data("itemnumber");
|
|
let item_level_hold =
|
|
this.item_level_hold || $(this).data("item_level_hold");
|
|
let item_status = this.status || $(this).data("status");
|
|
let item_waiting = this.waiting || $(this).data("waiting");
|
|
let item_intransit =
|
|
this.intransit || $(this).data("intransit");
|
|
let error_message =
|
|
this.item_level_hold || $(this).data("item_level_hold")
|
|
? __(
|
|
"Cannot move a waiting, in transit, or item level hold"
|
|
)
|
|
: "";
|
|
let found_status = $(this).data("found");
|
|
if (!item_level_hold && (!item_waiting || !item_intransit)) {
|
|
$("#move_hold_biblio_selection table").append(
|
|
`<tr><td><input type="checkbox" name="move_hold_id" value="${reserve_id}" checked /><td>${reserve_id}</td><td>${__("Biblionumber:")} <a target="_blank" href="/cgi-bin/koha/reserve/request.pl?biblionumber=${reserve_biblionumber}">${reserve_biblionumber}</a></td><td>${error_message}</td></tr>`
|
|
);
|
|
} else {
|
|
$("#move_hold_biblio_selection table").append(
|
|
`<tr><td><input type="checkbox" name="move_hold_id" value="${reserve_id}" disabled /><td>${reserve_id}</td><td>${__("Biblionumber:")} <a target="_blank" href="/cgi-bin/koha/reserve/request.pl?biblionumber=${reserve_biblionumber}">${reserve_biblionumber}</a></td><td>${error_message}</td></tr>`
|
|
);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
function _append_patron_page_cancel_hold_modal_data(hold) {
|
|
$("#cancel_modal_form #inputs").append(
|
|
'<input type="hidden" name="rank-request" value="del">'
|
|
);
|
|
$("#cancel_modal_form #inputs").append(
|
|
'<input type="hidden" name="biblionumber" value="' +
|
|
hold.biblionumber +
|
|
'">'
|
|
);
|
|
$("#cancel_modal_form #inputs").append(
|
|
'<input type="hidden" name="borrowernumber" value="' +
|
|
hold.borrowernumber +
|
|
'">'
|
|
);
|
|
$("#cancel_modal_form #inputs").append(
|
|
'<input type="hidden" name="reserve_id" value="' + hold.hold + '">'
|
|
);
|
|
}
|
|
|
|
$(".group_selected_holds").click(function (e) {
|
|
if ($(".holds_table .select_hold:checked").length > 1) {
|
|
let selected_holds = get_selected_holds_data();
|
|
const group_ids = JSON.parse(selected_holds)
|
|
.filter(hold => hold.hold_group_id)
|
|
.map(hold => hold.hold_group_id);
|
|
|
|
if (group_ids.length > 0) {
|
|
$("#group-modal .modal-body").prepend(
|
|
'<div class="alert alert-warning">' +
|
|
__(
|
|
"Already grouped holds will be moved to the new group"
|
|
) +
|
|
"</div>"
|
|
);
|
|
}
|
|
|
|
$("#group-modal").modal("show");
|
|
}
|
|
return false;
|
|
});
|
|
|
|
if (holds_table_patron_page()) {
|
|
$("#cancelModalConfirmBtn").click(function (e) {
|
|
e.preventDefault();
|
|
let formInputs = {};
|
|
formInputs["reserve_id"] = $(
|
|
"#cancel_modal_form :input[name='reserve_id']"
|
|
)
|
|
.map(function () {
|
|
return $(this).val();
|
|
})
|
|
.get();
|
|
formInputs["cancellation-reason"] = $(
|
|
"#cancel_modal_form :input[name='cancellation-reason']"
|
|
).val();
|
|
cancel_holds(
|
|
formInputs["reserve_id"],
|
|
formInputs["cancellation-reason"]
|
|
)
|
|
.success(function () {
|
|
holdsTable.api().ajax.reload();
|
|
})
|
|
.fail(function (jqXHR) {
|
|
$("#cancelModal .modal-body").prepend(
|
|
'<div class="alert alert-danger">' +
|
|
jqXHR.responseJSON.error +
|
|
"</div>"
|
|
);
|
|
$("#cancelModalConfirmBtn").prop("disabled", true);
|
|
})
|
|
.done(function () {
|
|
$("#cancelModal").modal("hide");
|
|
if ($(".select_hold_all").prop("checked")) {
|
|
$(".select_hold_all").click();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function cancel_holds(hold_ids, cancellation_reason) {
|
|
return $.ajax({
|
|
method: "DELETE",
|
|
url: "/api/v1/holds/cancellation_bulk",
|
|
contentType: "application/json",
|
|
data: JSON.stringify({
|
|
hold_ids: hold_ids,
|
|
cancellation_reason: cancellation_reason,
|
|
}),
|
|
});
|
|
}
|
|
if (holds_table_patron_page()) {
|
|
$("#cancelModal").on("hidden.bs.modal", function () {
|
|
$("#cancelModal .modal-body .alert-danger").remove();
|
|
$("#cancelModalConfirmBtn").prop("disabled", false);
|
|
if (holdsTable) {
|
|
holdsTable.api().ajax.reload();
|
|
}
|
|
});
|
|
}
|
|
|
|
$("#group-modal-submit").click(function (e) {
|
|
e.preventDefault();
|
|
let selected_holds = get_selected_holds_data();
|
|
|
|
const hold_ids = JSON.parse(selected_holds).map(hold => hold.hold);
|
|
|
|
try {
|
|
group_holds(hold_ids)
|
|
.success(function () {
|
|
holdsTable.api().ajax.reload();
|
|
})
|
|
.fail(function (jqXHR) {
|
|
$("#group-modal .modal-body").prepend(
|
|
'<div class="alert alert-danger">' +
|
|
jqXHR.responseJSON.error +
|
|
"</div>"
|
|
);
|
|
$("#group-modal-submit").prop("disabled", true);
|
|
})
|
|
.done(function () {
|
|
$("#group-modal").modal("hide");
|
|
$(".select_hold_all").click();
|
|
});
|
|
} catch (error) {
|
|
if (error.status === 404) {
|
|
alert(__("Unable to group, hold not found."));
|
|
} else {
|
|
alert(
|
|
__(
|
|
"Your request could not be processed. Check the logs for details."
|
|
)
|
|
);
|
|
}
|
|
}
|
|
return false;
|
|
});
|
|
|
|
function group_holds(hold_ids) {
|
|
return $.ajax({
|
|
method: "POST",
|
|
url: "/api/v1/patrons/" + borrowernumber + "/hold_groups",
|
|
contentType: "application/json",
|
|
data: JSON.stringify({ hold_ids: hold_ids, force_grouped: true }),
|
|
});
|
|
}
|
|
|
|
$("#group-modal").on("hidden.bs.modal", function () {
|
|
$("#group-modal .modal-body .alert-warning").remove();
|
|
$("#group-modal .modal-body .alert-danger").remove();
|
|
$("#group-modal-submit").prop("disabled", false);
|
|
});
|
|
|
|
function get_selected_holds_data() {
|
|
return (
|
|
"[" +
|
|
$(".holds_table .select_hold:checked")
|
|
.toArray()
|
|
.map(el =>
|
|
JSON.stringify({
|
|
hold: $(el).data("id"),
|
|
borrowernumber: $(el).data("borrowernumber"),
|
|
biblionumber: $(el).data("biblionumber"),
|
|
hold_group_id: $(el).data("hold-group-id"),
|
|
})
|
|
)
|
|
.join(",") +
|
|
"]"
|
|
);
|
|
}
|
|
});
|
|
|
|
async function load_patron_holds_table(biblio_id, split_data) {
|
|
const { name: split_name, value: split_value } = split_data;
|
|
let table_class = `patron_holds_table_${biblio_id}_${split_value}`;
|
|
const table_id = `#` + table_class;
|
|
let url = `/api/v1/holds/?q={"me.biblio_id":${biblio_id}`;
|
|
|
|
if (split_name === "branch" && split_value !== "any") {
|
|
url += `, "me.pickup_library_id":"${split_value}"`;
|
|
} else if (split_name === "itemtype" && split_value !== "any") {
|
|
url += `, "me.itemtype":"${split_value}"`;
|
|
} else if (split_name === "branch_itemtype") {
|
|
const [branch, itemtype] = split_value.split("_");
|
|
url +=
|
|
itemtype === "any"
|
|
? `, "me.pickup_library_id":"${branch}"`
|
|
: `, "me.pickup_library_id":"${branch}", "me.itemtype":"${itemtype}"`;
|
|
}
|
|
|
|
url += "}";
|
|
const totalHolds = $(table_id).data("total-holds");
|
|
const totalHoldsSelect = parseInt(totalHolds) + 1;
|
|
var holdsQueueTable = $(table_id).kohaTable(
|
|
{
|
|
createdRow: function (row, data, dataIndex) {
|
|
if (data.suspended) {
|
|
$(row).addClass("suspend");
|
|
}
|
|
},
|
|
language: {
|
|
infoFiltered: "",
|
|
},
|
|
ajax: {
|
|
url: url,
|
|
},
|
|
embed: [
|
|
"patron",
|
|
"item",
|
|
"item_group",
|
|
"item_level_holds_count",
|
|
"iso18626_request",
|
|
],
|
|
columnDefs: [
|
|
{
|
|
targets: [2, 3],
|
|
className: "dt-body-nowrap",
|
|
},
|
|
],
|
|
columns: [
|
|
{
|
|
data: "hold_id",
|
|
orderable: false,
|
|
searchable: false,
|
|
render: function (data, type, row, meta) {
|
|
return (
|
|
'<input type="checkbox" class="select_hold ' +
|
|
table_class +
|
|
'" data-id="' +
|
|
data +
|
|
'" data-borrowernumber="' +
|
|
row.patron_id +
|
|
'" data-biblionumber="' +
|
|
biblio_id +
|
|
'" data-itemnumber="' +
|
|
row.item_id +
|
|
'" data-hold-group-id="' +
|
|
row.hold_group_id +
|
|
'" data-item_level_hold="' +
|
|
row.item_level_holds_count +
|
|
'" data-waiting="' +
|
|
(row.status === "W" ? "1" : "") +
|
|
'" data-intransit="' +
|
|
(row.status === "T" ? "1" : "") +
|
|
'" data-status="' +
|
|
(row.status ? row.status : "") +
|
|
'"/>'
|
|
);
|
|
},
|
|
},
|
|
{
|
|
data: "priority",
|
|
orderable: true,
|
|
searchable: false,
|
|
render: function (data, type, row, meta) {
|
|
let select =
|
|
'<select name="rank-request" class="rank-request ' +
|
|
table_class +
|
|
'" data-id="' +
|
|
row.hold_id;
|
|
if (
|
|
CAN_user_reserveforothers_modify_holds_priority &&
|
|
split_name == "any"
|
|
) {
|
|
for (var i = 0; i < totalHoldsSelect; i++) {
|
|
let selected;
|
|
let value;
|
|
let desc;
|
|
if (data == i && row.status == "T") {
|
|
select += '" disabled="disabled">';
|
|
selected = " selected='selected' ";
|
|
value = "T";
|
|
desc = __("In transit");
|
|
} else if (data == i && row.status == "P") {
|
|
select += '" disabled="disabled">';
|
|
selected = " selected='selected' ";
|
|
value = "P";
|
|
desc = __("In processing");
|
|
} else if (data == i && row.status == "W") {
|
|
select += '" disabled="disabled">';
|
|
selected = " selected='selected' ";
|
|
value = "W";
|
|
desc = __("Waiting");
|
|
} else if (data == i && !row.status) {
|
|
select += '">';
|
|
selected = " selected='selected' ";
|
|
value = data;
|
|
desc = data;
|
|
} else {
|
|
if (i != 0) {
|
|
select += '">';
|
|
value = i;
|
|
desc = i;
|
|
} else {
|
|
select += '">';
|
|
}
|
|
}
|
|
if (value) {
|
|
select +=
|
|
'<option value="' +
|
|
value +
|
|
'"' +
|
|
selected +
|
|
">" +
|
|
desc +
|
|
"</option>";
|
|
}
|
|
}
|
|
} else {
|
|
if (row.status == "T") {
|
|
select +=
|
|
'" disabled="disabled"><option value="T" selected="selected">' +
|
|
__("In transit") +
|
|
"</option></select>";
|
|
} else if (row.status == "P") {
|
|
select +=
|
|
'" disabled="disabled"><option value="P" selected="selected">' +
|
|
__("In processing") +
|
|
"</option></select>";
|
|
} else if (row.status == "W") {
|
|
select +=
|
|
'" disabled="disabled"><option value="W" selected="selected">' +
|
|
__("Waiting") +
|
|
"</option></select>";
|
|
} else {
|
|
if (
|
|
HoldsSplitQueue !== "nothing" &&
|
|
HoldsSplitQueueNumbering === "virtual"
|
|
) {
|
|
let virtualPriority =
|
|
meta.settings._iDisplayStart +
|
|
meta.row +
|
|
1;
|
|
select +=
|
|
'" disabled="disabled"><option value="' +
|
|
data +
|
|
'" selected="selected">' +
|
|
virtualPriority +
|
|
"</option></select>";
|
|
} else {
|
|
select +=
|
|
'" disabled="disabled"><option value="' +
|
|
data +
|
|
'" selected="selected">' +
|
|
data +
|
|
"</option></select>";
|
|
}
|
|
}
|
|
}
|
|
select += "</select>";
|
|
return select;
|
|
},
|
|
},
|
|
{
|
|
data: "",
|
|
orderable: false,
|
|
searchable: false,
|
|
render: function (data, type, row, meta) {
|
|
if (
|
|
row.status ||
|
|
!CAN_user_reserveforothers_modify_holds_priority
|
|
) {
|
|
return null;
|
|
}
|
|
let buttons =
|
|
'<a class="hold-arrow move-hold ' +
|
|
table_class +
|
|
'" title="' +
|
|
__("Move hold up") +
|
|
'" href="#" data-move-hold="up" data-priority="' +
|
|
row.priority +
|
|
'" reserve_id="' +
|
|
row.hold_id +
|
|
'"><i class="fa fa-lg icon-move-hold-up" aria-hidden="true"></i></a>';
|
|
buttons +=
|
|
'<a class="hold-arrow move-hold ' +
|
|
table_class +
|
|
'" title="' +
|
|
__("Move hold to top") +
|
|
'" href="#" data-move-hold="top" data-priority="' +
|
|
row.priority +
|
|
'" reserve_id="' +
|
|
row.hold_id +
|
|
'"><i class="fa fa-lg icon-move-hold-top" aria-hidden="true"></i></a>';
|
|
buttons +=
|
|
'<a class="hold-arrow move-hold ' +
|
|
table_class +
|
|
'" title="' +
|
|
__("Move hold to bottom") +
|
|
'" href="#" data-move-hold="bottom" data-priority="' +
|
|
row.priority +
|
|
'" reserve_id="' +
|
|
row.hold_id +
|
|
'"><i class="fa fa-lg icon-move-hold-bottom" aria-hidden="true"></i></a>';
|
|
buttons +=
|
|
'<a class="hold-arrow move-hold ' +
|
|
table_class +
|
|
'" title="' +
|
|
__("Move hold down") +
|
|
'" href="#" data-move-hold="down" data-priority="' +
|
|
row.priority +
|
|
'" reserve_id="' +
|
|
row.hold_id +
|
|
'"><i class="fa fa-lg icon-move-hold-down" aria-hidden="true"></i></a>';
|
|
return buttons;
|
|
},
|
|
},
|
|
{
|
|
data: HidePatronName
|
|
? "patron.cardnumber"
|
|
: "patron.surname:patron.firstname:patron.cardnumber",
|
|
orderable: true,
|
|
searchable: true,
|
|
render: function (data, type, row, meta) {
|
|
if (row.patron == null) {
|
|
let library = all_libraries.find(
|
|
library => library._id == row.pickup_library_id
|
|
);
|
|
return __("A patron from library %s").format(
|
|
library ? library._str : row.pickup_library_id
|
|
);
|
|
}
|
|
return $patron_to_html(row.patron, {
|
|
url: true,
|
|
display_cardnumber: true,
|
|
hide_patron_name: HidePatronName,
|
|
});
|
|
},
|
|
},
|
|
{
|
|
data: "notes",
|
|
orderable: false,
|
|
searchable: false,
|
|
render: function (data, type, row, meta) {
|
|
return data;
|
|
},
|
|
},
|
|
{
|
|
data: "hold_date",
|
|
orderable: true,
|
|
searchable: false,
|
|
render: function (data, type, row, meta) {
|
|
if (AllowHoldDateInFuture) {
|
|
return (
|
|
'<input type="text" class="holddate ' +
|
|
table_class +
|
|
'" value="' +
|
|
$date(data, { dateformat: "rfc3339" }) +
|
|
'" size="10" name="hold_date" data-id="' +
|
|
row.hold_id +
|
|
'" data-current-date="' +
|
|
data +
|
|
'"/>'
|
|
);
|
|
} else {
|
|
return $date(data);
|
|
}
|
|
},
|
|
},
|
|
{
|
|
data: "expiration_date",
|
|
orderable: true,
|
|
searchable: false,
|
|
render: function (data, type, row, meta) {
|
|
return (
|
|
'<input type="text" class="expirationdate ' +
|
|
table_class +
|
|
'" value="' +
|
|
$date(data, { dateformat: "rfc3339" }) +
|
|
'" size="10" name="expiration_date" data-id="' +
|
|
row.hold_id +
|
|
'" data-current-date="' +
|
|
data +
|
|
'"/>'
|
|
);
|
|
},
|
|
},
|
|
{
|
|
data: "pickup_library_id",
|
|
datatype: "coded_value:library",
|
|
orderable: true,
|
|
searchable: true,
|
|
render: function (data, type, row, meta) {
|
|
var branchSelect =
|
|
"<select priority=" +
|
|
row.priority +
|
|
' class="hold_location_select ' +
|
|
table_class +
|
|
'" data-id="' +
|
|
row.hold_id +
|
|
'" reserve_id="' +
|
|
row.hold_id +
|
|
'" name="pick-location" data-pickup-location-source="hold">';
|
|
var libraryname;
|
|
for (var i = 0; i < libraries.length; i++) {
|
|
var selectedbranch;
|
|
var setbranch;
|
|
if (libraries[i]._id == data) {
|
|
selectedbranch = " selected='selected' ";
|
|
setbranch = __(" (current) ");
|
|
libraryname = libraries[i]._str;
|
|
} else if (libraries[i].pickup_location == false) {
|
|
continue;
|
|
} else {
|
|
selectedbranch = "";
|
|
setbranch = "";
|
|
}
|
|
branchSelect +=
|
|
'<option value="' +
|
|
libraries[i]._id.escapeHtml() +
|
|
'"' +
|
|
selectedbranch +
|
|
">" +
|
|
libraries[i]._str.escapeHtml() +
|
|
setbranch +
|
|
"</option>";
|
|
}
|
|
branchSelect += "</select>";
|
|
if (row.status == "T") {
|
|
return __(
|
|
"Item being transferred to <strong>%s</strong>"
|
|
).format(libraryname);
|
|
} else if (row.status == "P") {
|
|
return __(
|
|
"Item being processed at <strong>%s</strong>"
|
|
).format(libraryname);
|
|
} else if (row.status == "W") {
|
|
return __(
|
|
"Item waiting at <strong>%s</strong> since %s"
|
|
).format(libraryname, $date(row.waiting_date));
|
|
} else {
|
|
return branchSelect;
|
|
}
|
|
},
|
|
},
|
|
{
|
|
data: "",
|
|
orderable: false,
|
|
searchable: false,
|
|
render: function (data, type, row, meta) {
|
|
const group_hold_message = row.hold_group_id
|
|
? `<div>(<a href="/cgi-bin/koha/reserve/hold-group.pl?hold_group_id=${row.hold_group_id}" class="hold-group">${__("Hold group")} ${row.hold_group_id}</a>)</div>`
|
|
: "";
|
|
|
|
const iso_message = row.iso18626_request
|
|
? create_iso18626_request_link(
|
|
row.iso18626_request.iso18626_request_id,
|
|
row.iso18626_request.status
|
|
)
|
|
: "";
|
|
|
|
const extras = group_hold_message + iso_message;
|
|
|
|
const non_priority_label = row.non_priority
|
|
? `<br/><i>${__("Non priority hold")}</i>`
|
|
: "";
|
|
|
|
// Handle status cases
|
|
if (row.status) {
|
|
return `<a href="/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=${row.biblio_id}&itemnumber=${row.item_id}">${row.item.external_id}</a>${non_priority_label}${extras}`;
|
|
}
|
|
|
|
// Handle item level holds
|
|
if (row.item_level) {
|
|
const barcode =
|
|
row.item.external_id || __("No barcode");
|
|
const itemLink = `<a href="/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=${row.biblio_id}&itemnumber=${row.item_id}">${barcode.escapeHtml ? barcode.escapeHtml() : barcode}</a>`;
|
|
|
|
if (row.item_level_holds_count >= 2) {
|
|
return `${__("Only item")} ${itemLink}${non_priority_label}${extras}`;
|
|
}
|
|
|
|
return `<select id="change_hold_type" class="change_hold_type ${table_class}" data-id="${row.hold_id}">
|
|
<option value="" selected>${__("Only item")} ${barcode}</option>
|
|
<option value="">${__("Next available")}</option>
|
|
</select>${non_priority_label}${extras}`;
|
|
}
|
|
|
|
// Handle item group
|
|
if (row.item_group_id) {
|
|
return (
|
|
__(
|
|
"Next available item from group <strong>%s</strong>"
|
|
).format(row.item_group.description) +
|
|
non_priority_label +
|
|
extras
|
|
);
|
|
}
|
|
|
|
// Default: Next available
|
|
let message = __("Next available");
|
|
return message + non_priority_label + extras;
|
|
},
|
|
},
|
|
{
|
|
data: "",
|
|
orderable: false,
|
|
searchable: false,
|
|
render: function (data, type, row, meta) {
|
|
if (
|
|
row.status ||
|
|
!CAN_user_reserveforothers_modify_holds_priority
|
|
) {
|
|
return null;
|
|
} else {
|
|
if (row.lowest_priority) {
|
|
return (
|
|
'<a class="hold-arrow toggle-lowest-priority ' +
|
|
table_class +
|
|
'" title="Remove lowest priority" href="#" data-op="cud-setLowestPriority" data-borrowernumber="' +
|
|
row.patron_id +
|
|
'" data-biblionumber="' +
|
|
biblio_id +
|
|
'" data-reserve_id="' +
|
|
row.hold_id +
|
|
'" data-date="' +
|
|
row.hold_date +
|
|
'"><i class="fa fa-lg fa-rotate-90 icon-unset-lowest" aria-hidden="true"></i></a>'
|
|
);
|
|
} else {
|
|
return (
|
|
'<a class="hold-arrow toggle-lowest-priority ' +
|
|
table_class +
|
|
'" title="Set lowest priority" href="#" data-op="cud-setLowestPriority" data-borrowernumber="' +
|
|
row.patron_id +
|
|
'" data-biblionumber="' +
|
|
biblio_id +
|
|
'" data-reserve_id="' +
|
|
row.hold_id +
|
|
'" data-date="' +
|
|
row.hold_date +
|
|
'"><i class="fa fa-lg fa-rotate-90 icon-set-lowest" aria-hidden="true"></i></a>'
|
|
);
|
|
}
|
|
}
|
|
},
|
|
},
|
|
{
|
|
data: "hold_id",
|
|
orderable: false,
|
|
searchable: false,
|
|
render: function (data, type, row, meta) {
|
|
return (
|
|
'<a class="cancel-hold deny ' +
|
|
table_class +
|
|
'" title="Cancel hold" data-id="' +
|
|
data +
|
|
'" data-biblionumber="' +
|
|
biblio_id +
|
|
'" data-borrowernumber="' +
|
|
row.patron_id +
|
|
'" href="#"><i class="fa fa-trash" aria-label="Cancel hold"></i></a>'
|
|
);
|
|
},
|
|
},
|
|
{
|
|
data: "hold_id",
|
|
orderable: false,
|
|
searchable: false,
|
|
render: function (data, type, row, meta) {
|
|
if (row.status) {
|
|
var link_value =
|
|
row.status == "T"
|
|
? __("Revert transit status")
|
|
: __("Revert waiting status");
|
|
return (
|
|
'<a class="btn btn-default submit-form-link" href="#" id="revert_hold_' +
|
|
data +
|
|
'" data-op="cud-move" data-where="down" data-first_priority="1" data-last_priority="' +
|
|
totalHolds +
|
|
'" data-prev_priority="0" data-next_priority="1" data-borrowernumber="' +
|
|
row.patron_id +
|
|
'" data-biblionumber="' +
|
|
biblio_id +
|
|
'" data-itemnumber="' +
|
|
row.item_id +
|
|
'" data-reserve_id="' +
|
|
row.hold_id +
|
|
'" data-date="' +
|
|
row.hold_date +
|
|
'" data-action="request.pl" data-method="post">' +
|
|
link_value +
|
|
"</a>"
|
|
);
|
|
} else {
|
|
let td = "";
|
|
if (SuspendHoldsIntranet) {
|
|
td +=
|
|
'<button class="btn btn-default btn-xs toggle-suspend ' +
|
|
table_class +
|
|
'" data-id="' +
|
|
data +
|
|
'" data-biblionumber="' +
|
|
biblio_id +
|
|
'" data-suspended="' +
|
|
row.suspended +
|
|
'">';
|
|
if (row.suspended) {
|
|
td +=
|
|
'<i class="fa fa-play" aria-hidden="true"></i> ' +
|
|
__("Unsuspend") +
|
|
"</button>";
|
|
} else {
|
|
td +=
|
|
'<i class="fa fa-pause" aria-hidden="true"></i> ' +
|
|
__("Suspend") +
|
|
"</button>";
|
|
}
|
|
if (AutoResumeSuspendedHolds) {
|
|
if (row.suspended) {
|
|
td +=
|
|
'<label for="suspend_until_' +
|
|
data +
|
|
'">' +
|
|
__("Suspend on") +
|
|
" </label>";
|
|
} else {
|
|
td +=
|
|
'<label for="suspend_until_' +
|
|
data +
|
|
'">' +
|
|
__("Suspend until") +
|
|
" </label>";
|
|
}
|
|
td +=
|
|
'<input type="text" name="suspend_until_' +
|
|
data +
|
|
'" data-id="' +
|
|
data +
|
|
'" size="10" value="' +
|
|
$date(row.suspended_until, {
|
|
dateformat: "rfc3339",
|
|
}) +
|
|
'" class="suspenddate ' +
|
|
table_class +
|
|
'" data-flatpickr-futuredate="true" data-suspend-date="' +
|
|
row.suspended_until +
|
|
'" />';
|
|
}
|
|
return td;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
},
|
|
},
|
|
{
|
|
data: "hold_id",
|
|
orderable: false,
|
|
searchable: false,
|
|
render: function (data, type, row, meta) {
|
|
if (row.status == "W" || row.status == "T") {
|
|
return (
|
|
'<a class="btn btn-default btn-xs printholdslip ' +
|
|
table_class +
|
|
'" data-reserve_id="' +
|
|
data +
|
|
'">' +
|
|
__("Print slip") +
|
|
"</a>"
|
|
);
|
|
} else {
|
|
return "";
|
|
}
|
|
},
|
|
},
|
|
],
|
|
},
|
|
hold_table_settings
|
|
);
|
|
|
|
// Clear selectedHolds on page load
|
|
localStorage.removeItem("selectedHolds");
|
|
$(table_id).on("draw.dt", function () {
|
|
function updateMSGCounters() {
|
|
var MSG_CANCEL_SELECTED = __("Cancel selected (%s)");
|
|
var MSG_MOVE_SELECTED = __("Move selected (%s)");
|
|
var MSG_RECALL_SELECTED = __("Convert selected to recall (%s)");
|
|
var MSG_CANCEL_ALERT = __(
|
|
"This action will cancel <span class='badge bg-danger'>%s</span> hold(s)."
|
|
);
|
|
var MSG_CONVERT_ALERT = __(
|
|
"This action will cancel <span class='badge bg-danger'>%s</span> hold(s) and create <span class='badge bg-danger'>%s</span> recall(s)."
|
|
);
|
|
var selectedCount = JSON.parse(
|
|
localStorage.selectedHolds || "[]"
|
|
).length;
|
|
$(".cancel_selected_holds").html(
|
|
MSG_CANCEL_SELECTED.format(selectedCount)
|
|
);
|
|
$(".move_selected_holds").html(
|
|
MSG_MOVE_SELECTED.format(selectedCount)
|
|
);
|
|
$("#cancel_hold_alert").html(
|
|
MSG_CANCEL_ALERT.format(selectedCount)
|
|
);
|
|
$("#convert_hold_alert").html(
|
|
MSG_CONVERT_ALERT.format(selectedCount, selectedCount)
|
|
);
|
|
$(".convert_selected_holds").html(
|
|
MSG_RECALL_SELECTED.format(selectedCount)
|
|
);
|
|
if (selectedCount > 0) {
|
|
$("#cancel_hold_alert").show();
|
|
$("#convert_hold_alert").show();
|
|
} else {
|
|
$("#cancel_hold_alert").hide();
|
|
$("#convert_hold_alert").hide();
|
|
}
|
|
}
|
|
|
|
function updateMoveButtons() {
|
|
var checked_holds = JSON.parse(localStorage.selectedHolds || "[]");
|
|
var checked_count = checked_holds.length;
|
|
|
|
var item_level_count = checked_holds.filter(function (hold) {
|
|
return hold.item_level_hold > 0;
|
|
}).length;
|
|
|
|
var record_level_count = checked_holds.filter(function (hold) {
|
|
return hold.item_level_hold === 0;
|
|
}).length;
|
|
|
|
$(".move_hold_item").toggleClass("disabled", item_level_count <= 0);
|
|
$(".move_hold_biblio").toggleClass(
|
|
"disabled",
|
|
record_level_count <= 0
|
|
);
|
|
$(".move_selected_holds").prop("disabled", !checked_count);
|
|
}
|
|
|
|
// Always deselect the "select all" checkbox when the page changes
|
|
$(".holds_table .select_hold_all").prop("checked", false);
|
|
updateMSGCounters();
|
|
$(".holds_table .select_hold." + table_class).each(function () {
|
|
const selected = JSON.parse(localStorage.selectedHolds || "[]");
|
|
const holdId = $(this).data("id");
|
|
if (selected.some(s => s.hold === holdId)) {
|
|
$(this).prop("checked", true);
|
|
$(this).parent().parent().addClass("selected");
|
|
var table = $(this).closest(".holds_table");
|
|
var count = $(
|
|
".select_hold." + table_class + ":not(:checked)",
|
|
table
|
|
).length;
|
|
$(".select_hold_all." + table_class, table).prop(
|
|
"checked",
|
|
!count
|
|
);
|
|
}
|
|
});
|
|
$(".holds_table .select_hold_all").on("click", function () {
|
|
var table = $(this).closest(".holds_table");
|
|
var isChecked = $(this).prop("checked");
|
|
var allCheckboxes = table
|
|
.DataTable()
|
|
.rows({ search: "applied" })
|
|
.nodes();
|
|
let selected = JSON.parse(localStorage.selectedHolds || "[]");
|
|
let pageHolds = [];
|
|
|
|
$("input.select_hold", allCheckboxes).each(function () {
|
|
let hold_data = {
|
|
hold: $(this).data("id"),
|
|
borrowernumber: $(this).data("borrowernumber"),
|
|
biblionumber: $(this).data("biblionumber"),
|
|
itemnumber: $(this).data("itemnumber"),
|
|
waiting: $(this).data("waiting"),
|
|
intransit: $(this).data("intransit"),
|
|
status: $(this).data("status"),
|
|
item_level_hold: $(this).data("item_level_hold"),
|
|
hold_group_id: $(this).data("hold-group-id"),
|
|
};
|
|
pageHolds.push(hold_data);
|
|
});
|
|
|
|
if (isChecked) {
|
|
// Add all page holds to the selection, avoiding duplicates
|
|
pageHolds.forEach(hold => {
|
|
if (!selected.some(s => s.hold === hold.hold)) {
|
|
selected.push(hold);
|
|
}
|
|
});
|
|
$("input.select_hold", allCheckboxes).prop("checked", true);
|
|
$("input.select_hold", allCheckboxes).each(function () {
|
|
$(this).parent().parent().addClass("selected");
|
|
});
|
|
} else {
|
|
// Remove all page holds from the selection
|
|
const pageIds = pageHolds.map(h => h.hold);
|
|
selected = selected.filter(s => !pageIds.includes(s.hold));
|
|
$("input.select_hold", allCheckboxes).prop("checked", false);
|
|
$("input.select_hold", allCheckboxes).each(function () {
|
|
$(this).parent().parent().removeClass("selected");
|
|
});
|
|
}
|
|
|
|
localStorage.selectedHolds = JSON.stringify(selected);
|
|
updateMoveButtons();
|
|
updateMSGCounters();
|
|
});
|
|
$(".holds_table .select_hold").on("click", function () {
|
|
let selected = JSON.parse(localStorage.selectedHolds || "[]");
|
|
const hold_data = {
|
|
hold: $(this).data("id"),
|
|
borrowernumber: $(this).data("borrowernumber"),
|
|
biblionumber: $(this).data("biblionumber"),
|
|
itemnumber: $(this).data("itemnumber"),
|
|
waiting: $(this).data("waiting"),
|
|
intransit: $(this).data("intransit"),
|
|
status: $(this).data("status"),
|
|
item_level_hold: $(this).data("item_level_hold"),
|
|
hold_group_id: $(this).data("hold-group-id"),
|
|
};
|
|
if ($(this).is(":checked")) {
|
|
if (!selected.some(s => s.hold === hold_data.hold)) {
|
|
selected.push(hold_data);
|
|
}
|
|
} else {
|
|
selected = selected.filter(s => s.hold !== hold_data.hold);
|
|
}
|
|
localStorage.selectedHolds = JSON.stringify(selected);
|
|
updateMoveButtons();
|
|
updateMSGCounters();
|
|
var table = $(this).parents(".holds_table");
|
|
var count = $(".select_hold:not(:checked)", table).length;
|
|
$(".select_hold_all", table).prop("checked", !count);
|
|
$(this).parent().parent().toggleClass("selected");
|
|
});
|
|
$(".cancel-hold." + table_class).on("click", function (e) {
|
|
e.preventDefault();
|
|
cancel_link = $(this);
|
|
$("#cancel_modal_form #inputs").empty();
|
|
let reserve_id = cancel_link.data("id");
|
|
let biblionumber = cancel_link.data("biblionumber");
|
|
_append_patron_page_cancel_hold_modal_data({
|
|
hold: reserve_id,
|
|
biblionumber: biblionumber,
|
|
borrowernumber: cancel_link.data("borrowernumber"),
|
|
});
|
|
$("#cancelModal").modal("show");
|
|
});
|
|
$(".cancel_selected_holds").click(function (e) {
|
|
e.preventDefault();
|
|
const selectedHolds = JSON.parse(
|
|
localStorage.selectedHolds || "[]"
|
|
);
|
|
if (selectedHolds.length) {
|
|
$("#cancel_modal_form #inputs").empty();
|
|
|
|
selectedHolds.forEach(function (hold) {
|
|
_append_patron_page_cancel_hold_modal_data(hold);
|
|
});
|
|
|
|
//delete localStorage.selectedHolds;
|
|
$("#cancelModal").modal("show");
|
|
}
|
|
return false;
|
|
});
|
|
$(".convert_selected_holds").click(function (e) {
|
|
e.preventDefault();
|
|
const selectedHolds = JSON.parse(
|
|
localStorage.selectedHolds || "[]"
|
|
);
|
|
if (selectedHolds.length) {
|
|
$("#convert_to_recall_modal_form #inputs").empty();
|
|
$("#convert_to_recall_modal_form #inputs").append(
|
|
'<input type="hidden" name="op" value="cud-convertall">'
|
|
);
|
|
$(
|
|
"#convert_to_recall_modal_form #modal-cancellation-reason"
|
|
).val("RECALLED");
|
|
|
|
selectedHolds.forEach(function (hold) {
|
|
_append_patron_page_convert_hold_modal_data(hold);
|
|
});
|
|
|
|
//delete localStorage.selectedHolds;
|
|
$("#convertModal").modal("show");
|
|
}
|
|
return false;
|
|
});
|
|
function _append_patron_page_cancel_hold_modal_data(hold) {
|
|
$("#cancel_modal_form #inputs").append(
|
|
'<input type="hidden" name="rank-request" value="del">'
|
|
);
|
|
$("#cancel_modal_form #inputs").append(
|
|
'<input type="hidden" name="biblionumber" value="' +
|
|
hold.biblionumber +
|
|
'">'
|
|
);
|
|
$("#cancel_modal_form #inputs").append(
|
|
'<input type="hidden" name="borrowernumber" value="' +
|
|
hold.borrowernumber +
|
|
'">'
|
|
);
|
|
$("#cancel_modal_form #inputs").append(
|
|
'<input type="hidden" name="reserve_id" value="' +
|
|
hold.hold +
|
|
'">'
|
|
);
|
|
}
|
|
function _append_patron_page_convert_hold_modal_data(hold) {
|
|
$("#convert_to_recall_modal_form #inputs").append(
|
|
'<input type="hidden" name="rank-request" value="recall">'
|
|
);
|
|
$("#convert_to_recall_modal_form #inputs").append(
|
|
'<input type="hidden" name="biblionumber" value="' +
|
|
hold.biblionumber +
|
|
'">'
|
|
);
|
|
$("#convert_to_recall_modal_form #inputs").append(
|
|
'<input type="hidden" name="borrowernumber" value="' +
|
|
hold.borrowernumber +
|
|
'">'
|
|
);
|
|
$("#convert_to_recall_modal_form #inputs").append(
|
|
'<input type="hidden" name="reserve_id" value="' +
|
|
hold.hold +
|
|
'">'
|
|
);
|
|
}
|
|
// Remove any previously attached handlers
|
|
$("#cancelModalConfirmBtn").off("click");
|
|
// Attach the handler to the button
|
|
$("#cancelModalConfirmBtn").one("click", function (e) {
|
|
e.preventDefault();
|
|
let formInputs = {};
|
|
formInputs["reserve_id"] = $(
|
|
"#cancel_modal_form :input[name='reserve_id']"
|
|
)
|
|
.map(function () {
|
|
return $(this).val();
|
|
})
|
|
.get();
|
|
formInputs["cancellation-reason"] = $(
|
|
"#cancel_modal_form :input[name='cancellation-reason']"
|
|
).val();
|
|
cancel_holds(
|
|
formInputs["reserve_id"],
|
|
formInputs["cancellation-reason"]
|
|
)
|
|
.success(function () {
|
|
holdsQueueTable.api().ajax.reload(null, false);
|
|
})
|
|
.fail(function (jqXHR) {
|
|
$("#cancelModal .modal-body").prepend(
|
|
'<div class="alert alert-danger">' +
|
|
jqXHR.responseJSON.error +
|
|
"</div>"
|
|
);
|
|
$("#cancelModalConfirmBtn").prop("disabled", true);
|
|
})
|
|
.done(function () {
|
|
$("#cancelModal").modal("hide");
|
|
if ($(".select_hold_all").prop("checked")) {
|
|
$(".select_hold_all").click();
|
|
}
|
|
});
|
|
});
|
|
function cancel_holds(hold_ids, cancellation_reason) {
|
|
return $.ajax({
|
|
method: "DELETE",
|
|
url: "/api/v1/holds/cancellation_bulk",
|
|
contentType: "application/json",
|
|
data: JSON.stringify({
|
|
hold_ids: hold_ids,
|
|
cancellation_reason: cancellation_reason,
|
|
}),
|
|
});
|
|
}
|
|
$(
|
|
".holddate." + table_class + ", .expirationdate." + table_class
|
|
).flatpickr({
|
|
onReady: function (selectedDates, dateStr, instance) {
|
|
$(instance.altInput)
|
|
.wrap("<span class='flatpickr_wrapper'></span>")
|
|
.after(
|
|
$("<a/>")
|
|
.attr("href", "#")
|
|
.addClass("clear_date")
|
|
.addClass("fa fa-times")
|
|
.addClass("ps-2")
|
|
.on("click", function (e) {
|
|
e.preventDefault();
|
|
instance.clear();
|
|
})
|
|
.attr("aria-hidden", true)
|
|
);
|
|
},
|
|
onChange: function (selectedDates, dateStr, instance) {
|
|
let hold_id = $(instance.input).attr("data-id");
|
|
let fieldname = $(instance.input).attr("name");
|
|
let current_date = $(instance.input).attr("data-current-date");
|
|
dateStr = dateStr ? dateStr : null;
|
|
let req =
|
|
fieldname == "hold_date"
|
|
? { hold_date: dateStr }
|
|
: { expiration_date: dateStr };
|
|
if (current_date != dateStr) {
|
|
$.ajax({
|
|
method: "PATCH",
|
|
url: "/api/v1/holds/" + encodeURIComponent(hold_id),
|
|
contentType: "application/json",
|
|
data: JSON.stringify(req),
|
|
success: function (data) {
|
|
holdsQueueTable.api().ajax.reload(null, false);
|
|
$(instance.input).attr(
|
|
"data-current-date",
|
|
dateStr
|
|
);
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
holdsQueueTable.api().ajax.reload(null, false);
|
|
},
|
|
});
|
|
}
|
|
},
|
|
});
|
|
$(".suspenddate." + table_class).flatpickr({
|
|
onReady: function (selectedDates, dateStr, instance) {
|
|
$(instance.altInput)
|
|
.wrap("<span class='flatpickr_wrapper'></span>")
|
|
.after(
|
|
$("<a/>")
|
|
.attr("href", "#")
|
|
.addClass("clear_date")
|
|
.addClass("fa fa-times")
|
|
.addClass("ps-2")
|
|
.on("click", function (e) {
|
|
e.preventDefault();
|
|
instance.clear();
|
|
})
|
|
.attr("aria-hidden", true)
|
|
);
|
|
},
|
|
onChange: function (selectedDates, dateStr, instance) {
|
|
let hold_id = $(instance.input).attr("data-id");
|
|
let current_date = $(instance.input).attr("data-suspend-date");
|
|
dateStr = dateStr ? dateStr : null;
|
|
if (current_date != dateStr) {
|
|
let params =
|
|
dateStr !== null && dateStr !== ""
|
|
? JSON.stringify({ end_date: dateStr })
|
|
: null;
|
|
$.ajax({
|
|
method: "POST",
|
|
url:
|
|
"/api/v1/holds/" +
|
|
encodeURIComponent(hold_id) +
|
|
"/suspension",
|
|
contentType: "application/json",
|
|
data: params,
|
|
success: function (data) {
|
|
holdsQueueTable.api().ajax.reload(null, false);
|
|
$(instance.input).attr(
|
|
"data-suspend-date",
|
|
dateStr
|
|
);
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
holdsQueueTable.api().ajax.reload(null, false);
|
|
},
|
|
});
|
|
}
|
|
},
|
|
});
|
|
$(".toggle-suspend." + table_class).one("click", function (e) {
|
|
e.preventDefault();
|
|
const hold_id = $(this).data("id");
|
|
const suspended = $(this).attr("data-suspended");
|
|
const input = $(
|
|
`.suspenddate.` + table_class + `[data-id="${hold_id}"]`
|
|
);
|
|
const method = suspended == "true" ? "DELETE" : "POST";
|
|
let end_date = input.val() && method == "POST" ? input.val() : null;
|
|
let params =
|
|
end_date !== null && end_date !== ""
|
|
? JSON.stringify({ end_date: end_date })
|
|
: null;
|
|
$.ajax({
|
|
method: method,
|
|
url:
|
|
"/api/v1/holds/" +
|
|
encodeURIComponent(hold_id) +
|
|
"/suspension",
|
|
contentType: "application/json",
|
|
data: params,
|
|
success: function (data) {
|
|
holdsQueueTable.api().ajax.reload(null, false);
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
holdsQueueTable.api().ajax.reload(null, false);
|
|
alert(
|
|
"There was an error:" + textStatus + " " + errorThrown
|
|
);
|
|
},
|
|
});
|
|
});
|
|
$(".rank-request." + table_class).on("change", function (e) {
|
|
e.preventDefault();
|
|
const hold_id = $(this).data("id");
|
|
let priority = e.target.value;
|
|
// Replace select with spinner
|
|
const $select = $(this);
|
|
const $spinner = $(
|
|
'<img class="rank-spinner" src="/intranet-tmpl/prog/img/spinner-small.gif" alt="Loading..." style="display:block;margin:0 auto;vertical-align:middle;">'
|
|
);
|
|
$select.hide().after($spinner);
|
|
$.ajax({
|
|
method: "PUT",
|
|
url:
|
|
"/api/v1/holds/" +
|
|
encodeURIComponent(hold_id) +
|
|
"/priority",
|
|
data: JSON.stringify(priority),
|
|
success: function (data) {
|
|
holdsQueueTable.api().ajax.reload(null, false);
|
|
$spinner.remove();
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
alert(
|
|
"There was an error:" + textStatus + " " + errorThrown
|
|
);
|
|
$select.show();
|
|
$spinner.remove();
|
|
},
|
|
});
|
|
});
|
|
$(".move-hold." + table_class).one("click", function (e) {
|
|
e.preventDefault();
|
|
let toPosition = $(this).attr("data-move-hold");
|
|
let priority = $(this).attr("data-priority");
|
|
var res_id = $(this).attr("reserve_id");
|
|
var moveTo;
|
|
const $spinner = $(
|
|
'<img class="rank-spinner" src="/intranet-tmpl/prog/img/spinner-small.gif" alt="Loading..." style="display:block;margin:0 auto;vertical-align:middle;">'
|
|
);
|
|
if (toPosition == "up") {
|
|
moveTo = parseInt(priority) - 1;
|
|
}
|
|
if (toPosition == "down") {
|
|
moveTo = parseInt(priority) + 1;
|
|
}
|
|
if (toPosition == "top") {
|
|
moveTo = 1;
|
|
}
|
|
if (toPosition == "bottom") {
|
|
moveTo = totalHolds;
|
|
}
|
|
$(this).parent().html($spinner);
|
|
$.ajax({
|
|
method: "PUT",
|
|
url:
|
|
"/api/v1/holds/" + encodeURIComponent(res_id) + "/priority",
|
|
data: JSON.stringify(moveTo),
|
|
success: function (data) {
|
|
$spinner.remove();
|
|
holdsQueueTable.api().ajax.reload(null, false);
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
$spinner.remove();
|
|
alert(
|
|
"There was an error:" + textStatus + " " + errorThrown
|
|
);
|
|
},
|
|
});
|
|
});
|
|
$(".toggle-lowest-priority." + table_class).one("click", function (e) {
|
|
e.preventDefault();
|
|
var res_id = $(this).attr("data-reserve_id");
|
|
$.ajax({
|
|
method: "PUT",
|
|
url:
|
|
"/api/v1/holds/" +
|
|
encodeURIComponent(res_id) +
|
|
"/lowest_priority",
|
|
success: function (data) {
|
|
holdsQueueTable.api().ajax.reload(null, false);
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
alert(
|
|
"There was an error:" + textStatus + " " + errorThrown
|
|
);
|
|
},
|
|
});
|
|
});
|
|
$(".hold_location_select." + table_class).on("change", function () {
|
|
$(this).prop("disabled", true);
|
|
var cur_select = $(this);
|
|
var res_id = $(this).attr("reserve_id");
|
|
$(this).after(
|
|
'<div id="updating_reserveno' +
|
|
res_id +
|
|
'" class="waiting"><img src="/intranet-tmpl/prog/img/spinner-small.gif" alt="" /><span class="waiting_msg"></span></div>'
|
|
);
|
|
let api_url =
|
|
"/api/v1/holds/" +
|
|
encodeURIComponent(res_id) +
|
|
"/pickup_location";
|
|
$.ajax({
|
|
method: "PUT",
|
|
url: api_url,
|
|
data: JSON.stringify({ pickup_library_id: $(this).val() }),
|
|
headers: { "x-koha-override": "any" },
|
|
success: function (data) {
|
|
holdsQueueTable.api().ajax.reload(null, false);
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
alert(
|
|
"There was an error:" + textStatus + " " + errorThrown
|
|
);
|
|
cur_select.prop("disabled", false);
|
|
$("#updating_reserveno" + res_id).remove();
|
|
cur_select.val(
|
|
cur_select.children('option[selected="selected"]').val()
|
|
);
|
|
},
|
|
});
|
|
});
|
|
$(".change_hold_type." + table_class).on("change", function () {
|
|
$(this).prop("disabled", true);
|
|
var cur_select = $(this);
|
|
var hold_id = $(this).attr("data-id");
|
|
$(this).after(
|
|
'<div id="updating_holdno' +
|
|
hold_id +
|
|
'" class="waiting"><img src="/intranet-tmpl/prog/img/spinner-small.gif" alt="" /><span class="waiting_msg"></span></div>'
|
|
);
|
|
let api_url = "/api/v1/holds/" + encodeURIComponent(hold_id);
|
|
$.ajax({
|
|
method: "PATCH",
|
|
url: api_url,
|
|
data: JSON.stringify({ item_id: null, item_level: false }),
|
|
headers: { "x-koha-override": "any" },
|
|
contentType: "application/json",
|
|
success: function (data) {
|
|
holdsQueueTable.api().ajax.reload(null, false);
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
alert(
|
|
"There was an error:" + textStatus + " " + errorThrown
|
|
);
|
|
cur_select.prop("disabled", false);
|
|
$("#updating_holdno" + hold_id).remove();
|
|
cur_select.val(
|
|
cur_select.children('option[selected="selected"]').val()
|
|
);
|
|
},
|
|
});
|
|
});
|
|
$(".printholdslip." + table_class).one("click", function () {
|
|
var reserve_id = $(this).attr("data-reserve_id");
|
|
window.open(
|
|
"/cgi-bin/koha/circ/hold-transfer-slip.pl?reserve_id=" +
|
|
reserve_id
|
|
);
|
|
return false;
|
|
});
|
|
updateMSGCounters();
|
|
});
|
|
}
|