5 [% INCLUDE 'doc-head-open.inc' %]
6 <title>Bookings for [% INCLUDE 'biblio-title-head.inc' %] › Bookings › Circulation › Koha</title>
7 [% INCLUDE 'doc-head-close.inc' %]
10 <body id="circ_request" class="catalog">
12 [% WRAPPER 'header.inc' %]
13 [% INCLUDE 'cat-search.inc' %]
16 [% WRAPPER 'sub-header.inc' %]
17 [% WRAPPER breadcrumbs %]
18 [% WRAPPER breadcrumb_item %]
19 <a href="/cgi-bin/koha/catalogue/search.pl">Catalog</a>
21 [% WRAPPER breadcrumb_item %]
22 [% INCLUDE 'biblio-title.inc' link = 1 %]
24 [% WRAPPER breadcrumb_item bc_active= 1 %]
27 [% END #/ WRAPPER breadcrumbs %]
28 [% END #/ WRAPPER sub-header.inc %]
30 <div class="main container-fluid">
32 <div class="col-sm-10 col-sm-push-2">
35 [% INCLUDE 'cat-toolbar.inc' %]
36 <h1>Bookings for [% INCLUDE 'biblio-title-head.inc' %]</h1>
37 <div class="page-section" id="bookings-timeline"></div>
38 <div class="page-section">
39 <table id="bookings_table"></table>
43 </div> <!-- /.col-sm-10.col-sm-push-2 -->
45 <div class="col-sm-2 col-sm-pull-10">
47 [% INCLUDE 'biblio-view-menu.inc' %]
49 </div> <!-- /.col-sm-2.col-sm-pull-10 -->
52 [% INCLUDE modals/cancel_booking.inc %]
54 [% MACRO jsinclude BLOCK %]
55 [% Asset.js("lib/dayjs/dayjs.min.js") | $raw %]
56 [% Asset.js("lib/vis-timeline/vis-timeline-graph2d.min.js") | $raw %]
57 [% INCLUDE 'datatables.inc' %]
58 [% INCLUDE 'columns_settings.inc' %]
59 [% INCLUDE 'calendar.inc' %]
60 [% INCLUDE 'select2.inc' %]
61 [% INCLUDE 'js-patron-format.inc' %]
62 [% INCLUDE 'js-date-format.inc' %]
63 [% Asset.js("js/place_booking_modal.js") | $raw %]
64 [% Asset.js("js/cancel_booking_modal.js") | $raw %]
66 var cancel_success = 0;
67 var update_success = 0;
70 $(document).ready(function(){
73 url: '/api/v1/biblios/[% biblionumber | uri %]/items?bookable=1',
77 var bookings = $.ajax({
78 url: '/api/v1/biblios/[% biblionumber | uri %]/bookings',
79 headers: { 'x-koha-embed': 'patron' },
84 $.when(items, bookings).then(
85 function(items, bookings){
86 var itemsSet = new vis.DataSet([{
88 content: _("Biblio level")
90 for (item of items[0]) {
93 content: _("Item") + " " + item.external_id,
97 var bookingsSet = new vis.DataSet();
98 for (booking of bookings[0]){
100 id: booking.booking_id,
101 booking: booking.booking_id,
102 patron: booking.patron_id,
103 start: dayjs(booking.start_date).toDate(),
104 end: dayjs(booking.end_date).toDate(),
105 content: $patron_to_html(booking.patron, {
106 display_cardnumber: true,
109 [% IF CAN_user_circulate_manage_bookings %]
110 editable: { remove: true, updateTime: true },
115 group: booking.item_id ? booking.item_id : 0
119 var container = document.getElementById('bookings-timeline');
122 verticalScroll: true,
127 timeAxis: {scale: 'day', step: 1},
128 dataAttributes: [ 'booking' ],
130 // always snap to full days, independent of the scale
131 snap: function (date, scale, step) {
132 let offset = date.getTimezoneOffset() * 60 * 1000;
133 let day = 24 * 60 * 60 * 1000;
134 let seconds = Math.round(date / day) * day;
135 return seconds + offset;
138 // prevent overlapping bookings
139 onMoving: function (item, callback) {
140 var overlapping = bookingsSet.get({
141 filter: function(testItem) {
142 if (testItem.id == item.id) {
145 if (testItem.group != item.group) {
148 return ((item.start < testItem.end) && (item.end > testItem.start));
152 if (overlapping.length == 0) {
158 onMove: function (data, callback) {
159 let startDate = dayjs(data.start);
161 // set end datetime hours and minutes to the end of the day
162 let endDate = dayjs(data.end).endOf('day');
164 $('#placeBookingModal').modal('show', $('<button data-booking="'+data.id+'" data-biblionumber="[% biblionumber | uri %]" data-itemnumber="'+data.group+'" data-patron="'+data.patron+'" data-start_date="'+startDate.toISOString()+'" data-end_date="'+endDate.toISOString()+'">'));
165 $('#placeBookingModal').on('hide.bs.modal', function(e) {
166 if (update_success) {
174 onRemove: function (item, callback) {
175 $('#cancelBookingModal').modal('show', $('<button data-booking="'+item.id+'">'));
176 $('#cancelBookingModal').on('hide.bs.modal', function(e) {
177 if (cancel_success) {
186 timeline = new vis.Timeline(container, bookingsSet, itemsSet, options);
188 function(jqXHR, textStatus, errorThrown){
189 console.log("Fetch failed");
193 var bookings_table_url = "/api/v1/biblios/[% biblionumber | uri %]/bookings";
194 bookings_table = $('#bookings_table').kohaTable({
196 "url": bookings_table_url
203 "data": "booking_id",
204 "title": _("Booking ID"),
208 "data": "item.external_id",
212 "defaultContent": _("Any item"),
213 "render": function(data,type,row,meta) {
215 return row.item.external_id + " (" + row.booking_id + ")";
222 "data": "patron.firstname:patron.surname",
223 "title": _("Patron"),
226 "render": function(data, type, row, meta) {
227 return $patron_to_html(row.patron, {
228 display_cardnumber: true,
234 "data": "start_date",
235 "title": _("Start date"),
238 "render": function(data, type, row, meta) {
239 return $date(row.start_date);
244 "title": _("End date"),
247 "render": function(data, type, row, meta) {
248 return $date(row.end_date);
253 "title": _("Actions"),
257 "render": function(data, type, row, meta) {
259 [% IF CAN_user_circulate_manage_bookings %]
260 result += '<button type="button" class="btn btn-default btn-xs edit-action" data-toggle="modal" data-target="#placeBookingModal" data-booking="'+row.booking_id+'" data-biblionumber="[% biblionumber | uri %]" data-itemnumber="'+row.item_id+'" data-patron="'+row.patron_id+'" data-start_date="'+row.start_date+'" data-end_date="'+row.end_date+'"><i class="fa fa-pencil" aria-hidden="true"></i> '+_("Edit")+'</button>';
261 result += '<button type="button" class="btn btn-default btn-xs cancel-action" data-toggle="modal" data-target="#cancelBookingModal" data-booking="'+row.booking_id+'"><i class="fa fa-trash" aria-hidden="true"></i> '+_("Cancel")+'</button>';
271 [% INCLUDE 'intranet-bottom.inc' %]