Bug 40544: Move some bundles related code out of document.ready

On bug 40127 we moved the build_items_table calls outside of document
ready, but createChild is defined in document.ready (and so defined too
late).

This patches fix the following JS error:
"Uncaught ReferenceError: createChild is not defined"

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Caroline Cyr La Rose <caroline.cyr-la-rose@inlibro.com>

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
(cherry picked from commit 60b7c238a8)
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
This commit is contained in:
Jonathan Druart 2025-07-30 10:12:27 +02:00 committed by Paul Derscheid
parent a0af2fc321
commit 4caa113d62
No known key found for this signature in database
GPG Key ID: C25F9FD44CED595B
1 changed files with 277 additions and 277 deletions

View File

@ -1768,317 +1768,317 @@
[% END # /IF bundlesEnabled %]
});
$(document).ready(function() {
[% IF bundlesEnabled %] // Bundle handling
function createChild ( row, itemnumber, duedate ) {
// Toolbar
var bundle_toolbar = $('<div id="toolbar" class="btn-toolbar"></div>');
bundle_toolbar.append('<a class="btn btn-default" data-bs-toggle="modal" data-bs-target="#addToBundleModal" data-item="' + itemnumber + '"><i class="fa fa-plus"></i> ' + _("Add to bundle") + '</a>');
bundle_toolbar.append('<a class="btn btn-default" data-bs-toggle="modal" data-bs-target="#removeFromBundleModal" data-item="' + itemnumber + '"><i class="fa fa-minus"></i> ' + _("Remove from bundle") + '</a>');
[% IF bundlesEnabled %] // Bundle handling
function createChild ( row, itemnumber, duedate ) {
// Toolbar
var bundle_toolbar = $('<div id="toolbar" class="btn-toolbar"></div>');
bundle_toolbar.append('<a class="btn btn-default" data-bs-toggle="modal" data-bs-target="#addToBundleModal" data-item="' + itemnumber + '"><i class="fa fa-plus"></i> ' + _("Add to bundle") + '</a>');
bundle_toolbar.append('<a class="btn btn-default" data-bs-toggle="modal" data-bs-target="#removeFromBundleModal" data-item="' + itemnumber + '"><i class="fa fa-minus"></i> ' + _("Remove from bundle") + '</a>');
// This is the table we'll convert into a DataTable
var bundles_table = $('<table class="display tbundle" data-itemnumber="'+itemnumber+'" id="bundle_table_'+itemnumber+'" width="100%"/>');
// Toolbar
var bundle_toolbar = $('<div id="toolbar" class="btn-toolbar"></div>');
bundle_toolbar.append('<a class="btn btn-default" data-bs-toggle="modal" data-bs-target="#addToBundleModal" data-item="' + itemnumber + '"><i class="fa fa-plus"></i> ' + _("Add to bundle") + '</a>');
bundle_toolbar.append('<a class="btn btn-default" data-bs-toggle="modal" data-bs-target="#removeFromBundleModal" data-item="' + itemnumber + '"><i class="fa fa-minus"></i> ' + _("Remove from bundle") + '</a>');
// Display it the child row
row.child( bundle_toolbar.add(bundles_table), 'bundle' ).show();
// This is the table we'll convert into a DataTable
var bundles_table = $('<table class="display tbundle" data-itemnumber="'+itemnumber+'" id="bundle_table_'+itemnumber+'" width="100%"/>');
// Initialise as a DataTable
var bundle_table_url = "/api/v1/items/" + itemnumber + "/bundled_items?";
var bundle_table = bundles_table.kohaTable({
"ajax": {
"url": bundle_table_url
// Display it the child row
row.child( bundle_toolbar.add(bundles_table), 'bundle' ).show();
// Initialise as a DataTable
var bundle_table_url = "/api/v1/items/" + itemnumber + "/bundled_items?";
var bundle_table = bundles_table.kohaTable({
"ajax": {
"url": bundle_table_url
},
"embed": [
"biblio",
"return_claim.patron"
],
"order": [[ 1, "asc" ]],
"columnDefs": [ {
"targets": [0,1,2,3],
"render": function (data, type, row, meta) {
if ( data && type == 'display' ) {
return data.escapeHtml();
}
return data;
}
} ],
"columns": [
{
"data": "biblio.title:biblio.subtitle:biblio.medium",
"title": _("Title"),
"searchable": true,
"orderable": true,
"render": function(data, type, row, meta) {
return $biblio_to_html(row.biblio, { link: 1 });
}
},
"embed": [
"biblio",
"return_claim.patron"
],
"order": [[ 1, "asc" ]],
"columnDefs": [ {
"targets": [0,1,2,3],
"render": function (data, type, row, meta) {
if ( data && type == 'display' ) {
return data.escapeHtml();
}
return data;
}
} ],
"columns": [
{
"data": "biblio.title:biblio.subtitle:biblio.medium",
"title": _("Title"),
"searchable": true,
"orderable": true,
"render": function(data, type, row, meta) {
return $biblio_to_html(row.biblio, { link: 1 });
}
},
{
"data": "biblio.author",
"title": _("Author"),
"searchable": true,
"orderable": true,
},
{
"data": "copy_number",
"title": _("Copy number"),
"searchable": true,
"orderable": true,
},
{
"data": "callnumber",
"title": _("Callnumber"),
"searchable": true,
"orderable": true,
},
{
"data": "external_id",
"title": _("Barcode"),
"searchable": true,
"orderable": true,
},
{
"data": "lost_status:last_seen_date:return_claim.patron",
"title": _("Status"),
"searchable": false,
"orderable": false,
"render": function(data, type, row, meta) {
if ( row.lost_status == bundle_lost_value ) {
let out = '<span class="lost">' + _("Last seen") + ': ' + $date(row.last_seen_date) + '</span>';
if ( row.return_claim ) {
out = out + '<span class="claims_return">' + _("Claims returned by") + ': ' + $patron_to_html( row.return_claim.patron, { display_cardnumber: false, url: true } ) + '</span>';
}
return out;
{
"data": "biblio.author",
"title": _("Author"),
"searchable": true,
"orderable": true,
},
{
"data": "copy_number",
"title": _("Copy number"),
"searchable": true,
"orderable": true,
},
{
"data": "callnumber",
"title": _("Callnumber"),
"searchable": true,
"orderable": true,
},
{
"data": "external_id",
"title": _("Barcode"),
"searchable": true,
"orderable": true,
},
{
"data": "lost_status:last_seen_date:return_claim.patron",
"title": _("Status"),
"searchable": false,
"orderable": false,
"render": function(data, type, row, meta) {
if ( row.lost_status == bundle_lost_value ) {
let out = '<span class="lost">' + _("Last seen") + ': ' + $date(row.last_seen_date) + '</span>';
if ( row.return_claim ) {
out = out + '<span class="claims_return">' + _("Claims returned by") + ': ' + $patron_to_html( row.return_claim.patron, { display_cardnumber: false, url: true } ) + '</span>';
}
else if ( row.lost_status !== 0 ) {
return '<span class="lost">' + _("Lost") + ': ' + row.lost_status + '</span>';
}
return '<span class="available">' + _("Present") + '</span>';
return out;
}
else if ( row.lost_status !== 0 ) {
return '<span class="lost">' + _("Lost") + ': ' + row.lost_status + '</span>';
}
return '<span class="available">' + _("Present") + '</span>';
}
},
{
"data": function( row, type, val, meta ) {
var result;
if (duedate) {
result = '<button class="btn btn-default btn-xs remove disabled" role="button" data-itemnumber="'+row.item_id+'" title="%s"><i class="fa fa-minus" aria-hidden="true"></i> %s</button>\n'.format(_("This bundle is checked out, it cannot be modified"), _("Remove"));
} else {
result = '<button class="btn btn-default btn-xs remove" role="button" data-itemnumber="'+row.item_id+'"><i class="fa fa-minus" aria-hidden="true"></i> '+_("Remove")+'</button>\n';
}
return result;
},
{
"data": function( row, type, val, meta ) {
var result;
if (duedate) {
result = '<button class="btn btn-default btn-xs remove disabled" role="button" data-itemnumber="'+row.item_id+'" title="%s"><i class="fa fa-minus" aria-hidden="true"></i> %s</button>\n'.format(_("This bundle is checked out, it cannot be modified"), _("Remove"));
} else {
result = '<button class="btn btn-default btn-xs remove" role="button" data-itemnumber="'+row.item_id+'"><i class="fa fa-minus" aria-hidden="true"></i> '+_("Remove")+'</button>\n';
}
return result;
},
"title": _("Actions"),
"searchable": false,
"orderable": false,
"class": "no-export"
}
]
}, bundle_settings, 1);
$(".tbundle").on("click", ".remove:not(.disabled)", function(){
var bundle_table = $(this).closest('table');
var host_itemnumber = bundle_table.data('itemnumber');
var component_itemnumber = $(this).data('itemnumber');
var unlink_item_url = "/api/v1/items/" + host_itemnumber + "/bundled_items/" + component_itemnumber;
$.ajax({
type: "DELETE",
url: unlink_item_url,
success: function(){
bundle_table.DataTable({ 'retrieve': true }).draw(false);
}
});
"title": _("Actions"),
"searchable": false,
"orderable": false,
"class": "no-export"
}
]
}, bundle_settings, 1);
$(".tbundle").on("click", ".remove:not(.disabled)", function(){
var bundle_table = $(this).closest('table');
var host_itemnumber = bundle_table.data('itemnumber');
var component_itemnumber = $(this).data('itemnumber');
var unlink_item_url = "/api/v1/items/" + host_itemnumber + "/bundled_items/" + component_itemnumber;
$.ajax({
type: "DELETE",
url: unlink_item_url,
success: function(){
bundle_table.DataTable({ 'retrieve': true }).draw(false);
}
});
return;
}
var bundle_changed;
var bundle_form_active;
$("#addToBundleModal").on("shown.bs.modal", function(e){
var button = $(e.relatedTarget);
var item_id = button.data('item');
$("#addResult").replaceWith('<div id="addResult"></div>');
$("#addToBundleForm").attr('action', '/api/v1/items/' + item_id + '/bundled_items');
$("#external_id").focus();
bundle_changed = 0;
bundle_form_active = item_id;
});
function addToBundle (url, data) {
/* Send the data using post with external_id */
var posting = $.post({
url: url,
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
dataType: "json"
});
return;
}
const barcode = data.external_id;
const marc_link = data.marc_link;
var bundle_changed;
var bundle_form_active;
$("#addToBundleModal").on("shown.bs.modal", function(e){
var button = $(e.relatedTarget);
var item_id = button.data('item');
$("#addResult").replaceWith('<div id="addResult"></div>');
$("#addToBundleForm").attr('action', '/api/v1/items/' + item_id + '/bundled_items');
$("#external_id").focus();
bundle_changed = 0;
bundle_form_active = item_id;
});
/* Report the results */
posting.done(function(data) {
$('#addResult').replaceWith('<div id="addResult" class="alert alert-success">'+_("Success: Added '%s'").format(barcode)+'</div>');
$('#external_id').val('').focus();
bundle_changed = 1;
});
posting.fail(function(data) {
if ( data.status === 409 ) {
var response = data.responseJSON;
if ( response.error_code === 'already_bundled' ) {
$('#addResult').replaceWith('<div id="addResult" class="alert alert-warning">'+_("Warning: Item '%s' already attached").format(barcode)+'</div>');
} else if (response.error_code === 'bundle_checkout_out') {
$('#addResult').replaceWith('<div id="addResult" class="alert alert-danger">'+_("Failure: Bundle is currently checked out")+'</div>');
} else if (response.error_code === 'checked_out') {
const button = $('<button type="button">')
.addClass('btn btn-xs')
.text(_("Check in and add to bundle"))
.on('click', function () {
addToBundle(url, { external_id: barcode, force_checkin: true, marc_link: marc_link });
});
$('#addResult')
.empty()
.attr('class', 'alert alert-warning')
.append(__x('Warning: Item {barcode} is checked out', { barcode }))
.append(' ', button);
} else if (response.error_code === 'failed_checkin') {
$('#addResult')
.empty()
.attr('class', 'alert alert-danger')
.append(__x('Failure: Item {barcode} cannot be checked in', { barcode }))
} else if (response.error_code === 'reserved') {
const button = $('<button type="button">')
.addClass('btn btn-xs')
.text(_("Ignore holds and add to bundle"))
.on('click', function () {
addToBundle(url, { external_id: barcode, ignore_holds: true, marc_link: marc_link });
});
$('#addResult')
.empty()
.attr('class', 'alert alert-warning')
.append(__x('Warning: Item {barcode} is on hold', { barcode }))
.append(' ', button);
} else {
$('#addResult').replaceWith('<div id="addResult" class="alert alert-danger">'+_("Failure: Item '%s' belongs to another bundle").format(barcode)+'</div>');
}
} else if ( data.status === 404 ) {
$('#addResult').replaceWith('<div id="addResult" class="alert alert-danger">'+_("Failure: Item '%s' not found").format(barcode)+'</div>');
} else if ( data.status === 400 ) {
var response = data.responseJSON;
if ( response.error_code === "failed_nesting" ) {
$('#addResult').replaceWith('<div id="addResult" class="alert alert-danger">'+_("Failure: Item '%s' is a bundle and bundles cannot be nested").format(barcode)+'</div>');
} else {
$('#addResult').replaceWith('<div id="addResult" class="alert alert-danger">'+_("Failure: Check the logs for details.")+'</div>');
}
function addToBundle (url, data) {
/* Send the data using post with external_id */
var posting = $.post({
url: url,
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
dataType: "json"
});
const barcode = data.external_id;
const marc_link = data.marc_link;
/* Report the results */
posting.done(function(data) {
$('#addResult').replaceWith('<div id="addResult" class="alert alert-success">'+_("Success: Added '%s'").format(barcode)+'</div>');
$('#external_id').val('').focus();
bundle_changed = 1;
});
posting.fail(function(data) {
if ( data.status === 409 ) {
var response = data.responseJSON;
if ( response.error_code === 'already_bundled' ) {
$('#addResult').replaceWith('<div id="addResult" class="alert alert-warning">'+_("Warning: Item '%s' already attached").format(barcode)+'</div>');
} else if (response.error_code === 'bundle_checkout_out') {
$('#addResult').replaceWith('<div id="addResult" class="alert alert-danger">'+_("Failure: Bundle is currently checked out")+'</div>');
} else if (response.error_code === 'checked_out') {
const button = $('<button type="button">')
.addClass('btn btn-xs')
.text(_("Check in and add to bundle"))
.on('click', function () {
addToBundle(url, { external_id: barcode, force_checkin: true, marc_link: marc_link });
});
$('#addResult')
.empty()
.attr('class', 'alert alert-warning')
.append(__x('Warning: Item {barcode} is checked out', { barcode }))
.append(' ', button);
} else if (response.error_code === 'failed_checkin') {
$('#addResult')
.empty()
.attr('class', 'alert alert-danger')
.append(__x('Failure: Item {barcode} cannot be checked in', { barcode }))
} else if (response.error_code === 'reserved') {
const button = $('<button type="button">')
.addClass('btn btn-xs')
.text(_("Ignore holds and add to bundle"))
.on('click', function () {
addToBundle(url, { external_id: barcode, ignore_holds: true, marc_link: marc_link });
});
$('#addResult')
.empty()
.attr('class', 'alert alert-warning')
.append(__x('Warning: Item {barcode} is on hold', { barcode }))
.append(' ', button);
} else {
$('#addResult').replaceWith('<div id="addResult" class="alert alert-danger">'+_("Failure: Item '%s' belongs to another bundle").format(barcode)+'</div>');
}
} else if ( data.status === 404 ) {
$('#addResult').replaceWith('<div id="addResult" class="alert alert-danger">'+_("Failure: Item '%s' not found").format(barcode)+'</div>');
} else if ( data.status === 400 ) {
var response = data.responseJSON;
if ( response.error_code === "failed_nesting" ) {
$('#addResult').replaceWith('<div id="addResult" class="alert alert-danger">'+_("Failure: Item '%s' is a bundle and bundles cannot be nested").format(barcode)+'</div>');
} else {
$('#addResult').replaceWith('<div id="addResult" class="alert alert-danger">'+_("Failure: Check the logs for details.")+'</div>');
}
$('#external_id').val('').focus();
});
} else {
$('#addResult').replaceWith('<div id="addResult" class="alert alert-danger">'+_("Failure: Check the logs for details.")+'</div>');
}
$('#external_id').val('').focus();
});
}
$("#addToBundleForm").submit(function(event) {
/* stop form from submitting normally */
event.preventDefault();
const url = this.action;
const data = { external_id: this.elements.external_id.value, marc_link: this.elements.bundle_link.checked };
addToBundle(url, data);
});
$("#addToBundleModal").on("hidden.bs.modal", function(e){
if ( bundle_changed ) {
$('#bundle_table_'+bundle_form_active).DataTable({ 'retrieve': true }).ajax.reload();
}
bundle_form_active = 0;
bundle_changed = 0;
});
$("#addToBundleForm").submit(function(event) {
/* stop form from submitting normally */
event.preventDefault();
$("#removeFromBundleModal").on("shown.bs.modal", function(e){
var button = $(e.relatedTarget);
var item_id = button.data('item');
$("#removeResult").replaceWith('<div id="removeResult"></div>');
$("#removeFromBundleForm").attr('action', '/api/v1/items/' + item_id + '/bundled_items/');
$("#rm_external_id").focus();
bundle_changed = 0;
bundle_form_active = item_id;
});
const url = this.action;
const data = { external_id: this.elements.external_id.value, marc_link: this.elements.bundle_link.checked };
$("#removeFromBundleForm").submit(function(event) {
addToBundle(url, data);
});
/* stop form from submitting normally */
event.preventDefault();
$("#addToBundleModal").on("hidden.bs.modal", function(e){
if ( bundle_changed ) {
$('#bundle_table_'+bundle_form_active).DataTable({ 'retrieve': true }).ajax.reload();
}
bundle_form_active = 0;
bundle_changed = 0;
});
/* get the action attribute from the <form action=""> element */
var $form = $(this),
url = $form.attr('action');
$("#removeFromBundleModal").on("shown.bs.modal", function(e){
var button = $(e.relatedTarget);
var item_id = button.data('item');
$("#removeResult").replaceWith('<div id="removeResult"></div>');
$("#removeFromBundleForm").attr('action', '/api/v1/items/' + item_id + '/bundled_items/');
$("#rm_external_id").focus();
bundle_changed = 0;
bundle_form_active = item_id;
});
var barcode = $('#rm_external_id').val();
$("#removeFromBundleForm").submit(function(event) {
/* Fetch itemnumber using rm_external_id */
var itemReq = $.get('/api/v1/items', { q: JSON.stringify({
external_id: barcode
}) }, null, "json");
/* stop form from submitting normally */
event.preventDefault();
var itemnumber;
itemReq.done(function(data) {
if (data.length === 1) {
itemnumber = data[0].item_id;
/* get the action attribute from the <form action=""> element */
var $form = $(this),
url = $form.attr('action');
/* Remove link using fetch itemnumber */
var deleteReq = $.ajax( url + itemnumber, {
type : 'DELETE'
});
var barcode = $('#rm_external_id').val();
/* Fetch itemnumber using rm_external_id */
var itemReq = $.get('/api/v1/items', { q: JSON.stringify({
external_id: barcode
}) }, null, "json");
var itemnumber;
itemReq.done(function(data) {
if (data.length === 1) {
itemnumber = data[0].item_id;
/* Remove link using fetch itemnumber */
var deleteReq = $.ajax( url + itemnumber, {
type : 'DELETE'
});
/* Report the results */
deleteReq.done(function(data) {
var barcode = $('#rm_external_id').val();
$('#removeResult').replaceWith('<div id="removeResult" class="alert alert-success">'+_("Success: Removed '%s'").format(barcode)+'</div>');
$('#rm_external_id').val('').focus();
bundle_changed = 1;
});
deleteReq.fail(function(data) {
var barcode = $('#rm_external_id').val();
if ( data.status === 409 ) {
var response = data.responseJSON;
if (response.error_code === 'bundle_checkout_out') {
$('#removeResult').replaceWith('<div id="removeResult" class="alert alert-danger">'+_("Failure: Bundle is currently checked out")+'</div>');
} else if ( response.key === "PRIMARY" ) {
$('#removeResult').replaceWith('<div id="removeResult" class="alert alert-warning">'+_("Warning: Item '%s' already attached").format(barcode)+'</div>');
} else {
$('#removeResult').replaceWith('<div id="removeResult" class="alert alert-danger">'+_("Failure: Item '%s' belongs to another bundle").format(barcode)+'</div>');
}
} else if ( data.status === 404 ) {
$('#addResult').replaceWith('<div id="addResult" class="alert alert-danger">'+_("Failure: Item '%s' not found").format(barcode)+'</div>');
/* Report the results */
deleteReq.done(function(data) {
var barcode = $('#rm_external_id').val();
$('#removeResult').replaceWith('<div id="removeResult" class="alert alert-success">'+_("Success: Removed '%s'").format(barcode)+'</div>');
$('#rm_external_id').val('').focus();
bundle_changed = 1;
});
deleteReq.fail(function(data) {
var barcode = $('#rm_external_id').val();
if ( data.status === 409 ) {
var response = data.responseJSON;
if (response.error_code === 'bundle_checkout_out') {
$('#removeResult').replaceWith('<div id="removeResult" class="alert alert-danger">'+_("Failure: Bundle is currently checked out")+'</div>');
} else if ( response.key === "PRIMARY" ) {
$('#removeResult').replaceWith('<div id="removeResult" class="alert alert-warning">'+_("Warning: Item '%s' already attached").format(barcode)+'</div>');
} else {
$('#removeResult').replaceWith('<div id="removeResult" class="alert alert-danger">'+_("Failure: Check the logs for details")+'</div>');
$('#removeResult').replaceWith('<div id="removeResult" class="alert alert-danger">'+_("Failure: Item '%s' belongs to another bundle").format(barcode)+'</div>');
}
$('#rm_external_id').val('').focus();
});
} else {
$('#removeResult').replaceWith('<div id="removeResult" class="alert alert-danger">'+_("Failed: Barcode matched more than one item '%s'").format(barcode)+'</div>');
}
});
itemReq.fail(function(data) {
$('#removeResult').replaceWith('<div id="removeResult" class="alert alert-danger">'+_("Failed: Item not found '%s'").format(barcode)+'</div>');
$('#rm_external_id').val('').focus();
});
});
$("#removeFromBundleModal").on("hidden.bs.modal", function(e){
if ( bundle_changed ) {
$('#bundle_table_'+bundle_form_active).DataTable({ 'retrieve': true }).ajax.reload();
} else if ( data.status === 404 ) {
$('#addResult').replaceWith('<div id="addResult" class="alert alert-danger">'+_("Failure: Item '%s' not found").format(barcode)+'</div>');
} else {
$('#removeResult').replaceWith('<div id="removeResult" class="alert alert-danger">'+_("Failure: Check the logs for details")+'</div>');
}
$('#rm_external_id').val('').focus();
});
} else {
$('#removeResult').replaceWith('<div id="removeResult" class="alert alert-danger">'+_("Failed: Barcode matched more than one item '%s'").format(barcode)+'</div>');
}
bundle_form_active = 0;
bundle_changed = 0;
});
// End bundle handling
[% END # /IF bundlesEnabled %]
itemReq.fail(function(data) {
$('#removeResult').replaceWith('<div id="removeResult" class="alert alert-danger">'+_("Failed: Item not found '%s'").format(barcode)+'</div>');
$('#rm_external_id').val('').focus();
});
});
$("#removeFromBundleModal").on("hidden.bs.modal", function(e){
if ( bundle_changed ) {
$('#bundle_table_'+bundle_form_active).DataTable({ 'retrieve': true }).ajax.reload();
}
bundle_form_active = 0;
bundle_changed = 0;
});
// End bundle handling
[% END # /IF bundlesEnabled %]
$(document).ready(function() {
[% IF Koha.Preference('AcquisitionDetails') %]
var table_settings = [% TablesSettings.GetTableSettings('catalogue', 'detail', 'acquisitiondetails-table', 'json') | $raw %];
var acquisitiondetails_table = $("#orders").kohaTable(