View | Details | Raw Unified | Return to bug 36374
Collapse All | Expand All

(-)a/koha-tmpl/intranet-tmpl/prog/js/place_booking_modal.js (-292 / +441 lines)
Lines 1-60 Link Here
1
let dataFetched = false;
1
let dataFetched = false;
2
let bookable_items, bookings, checkouts, booking_id, booking_item_id, booking_patron;
2
let bookable_items,
3
3
    bookings,
4
$('#placeBookingModal').on('show.bs.modal', function(e) {
4
    checkouts,
5
5
    booking_id,
6
    booking_item_id,
7
    booking_patron;
8
9
$("#placeBookingModal").on("show.bs.modal", function (e) {
6
    // Get context
10
    // Get context
7
    let button = $(e.relatedTarget);
11
    let button = $(e.relatedTarget);
8
    let biblionumber = button.data('biblionumber');
12
    let biblionumber = button.data("biblionumber");
9
    $('#booking_biblio_id').val(biblionumber);
13
    $("#booking_biblio_id").val(biblionumber);
10
14
11
    let patron_id = button.data('patron') || 0;
15
    let patron_id = button.data("patron") || 0;
12
    booking_item_id = button.data('itemnumber');
16
    booking_item_id = button.data("itemnumber");
13
    let start_date = button.data('start_date');
17
    let start_date = button.data("start_date");
14
    let end_date = button.data('end_date');
18
    let end_date = button.data("end_date");
15
19
16
    // Get booking id if this is an edit
20
    // Get booking id if this is an edit
17
    booking_id = button.data('booking');
21
    booking_id = button.data("booking");
18
    if (booking_id) {
22
    if (booking_id) {
19
        $('#placeBookingLabel').html(__("Edit booking"));
23
        $("#placeBookingLabel").html(__("Edit booking"));
20
        $('#booking_id').val(booking_id);
24
        $("#booking_id").val(booking_id);
21
    } else {
25
    } else {
22
        $('#placeBookingLabel').html(__("Place booking"));
26
        $("#placeBookingLabel").html(__("Place booking"));
23
        // Ensure we don't accidentally update a booking
27
        // Ensure we don't accidentally update a booking
24
        $('#booking_id').val('');
28
        $("#booking_id").val("");
25
    }
29
    }
26
30
27
    // Patron select2
31
    // Patron select2
28
    $("#booking_patron_id").kohaSelect({
32
    $("#booking_patron_id").kohaSelect({
29
        dropdownParent: $(".modal-content", "#placeBookingModal"),
33
        dropdownParent: $(".modal-content", "#placeBookingModal"),
30
        width: '50%',
34
        width: "50%",
31
        dropdownAutoWidth: true,
35
        dropdownAutoWidth: true,
32
        allowClear: true,
36
        allowClear: true,
33
        minimumInputLength: 3,
37
        minimumInputLength: 3,
34
        ajax: {
38
        ajax: {
35
            url: '/api/v1/patrons',
39
            url: "/api/v1/patrons",
36
            delay: 250,
40
            delay: 250,
37
            dataType: 'json',
41
            dataType: "json",
38
            headers: {
42
            headers: {
39
                "x-koha-embed": "library"
43
                "x-koha-embed": "library",
40
            },
44
            },
41
            data: function(params) {
45
            data: function (params) {
42
                let q = buildPatronSearchQuery(params.term);
46
                let q = buildPatronSearchQuery(params.term);
43
                let query = {
47
                let query = {
44
                    'q': JSON.stringify(q),
48
                    q: JSON.stringify(q),
45
                    '_page': params.page,
49
                    _page: params.page,
46
                    '_order_by': '+me.surname,+me.firstname',
50
                    _order_by: "+me.surname,+me.firstname",
47
                };
51
                };
48
                return query;
52
                return query;
49
            },
53
            },
50
            processResults: function(data, params) {
54
            processResults: function (data, params) {
51
                let results = [];
55
                let results = [];
52
                data.results.forEach(function(patron) {
56
                data.results.forEach(function (patron) {
53
                    patron.id = patron.patron_id;
57
                    patron.id = patron.patron_id;
54
                    results.push(patron);
58
                    results.push(patron);
55
                });
59
                });
56
                return {
60
                return {
57
                    "results": results, "pagination": { "more": data.pagination.more }
61
                    results: results,
62
                    pagination: { more: data.pagination.more },
58
                };
63
                };
59
            },
64
            },
60
        },
65
        },
Lines 85-96 $('#placeBookingModal').on('show.bs.modal', function(e) { Link Here
85
                              __("years") +
90
                              __("years") +
86
                              "</span>"
91
                              "</span>"
87
                            : "") +
92
                            : "") +
88
                        (patron.library ?
93
                        (patron.library
89
                                " <span class=\"ac-library\">" +
94
                            ? ' <span class="ac-library">' +
90
                                escape_str(patron.library.name) +
95
                              escape_str(patron.library.name) +
91
                                "</span>"
96
                              "</span>"
92
                            : "") +
97
                            : "") +
93
                        "</small>"
98
                        "</small>",
94
                )
99
                )
95
                .addClass(loggedInClass);
100
                .addClass(loggedInClass);
96
            return $patron;
101
            return $patron;
Lines 99-145 $('#placeBookingModal').on('show.bs.modal', function(e) { Link Here
99
            if (!patron.surname) {
104
            if (!patron.surname) {
100
                return patron.text;
105
                return patron.text;
101
            }
106
            }
102
            return (
107
            return patron.surname + ", " + patron.firstname;
103
                patron.surname + ", " + patron.firstname
104
            );
105
        },
108
        },
106
        placeholder: __("Search for a patron")
109
        placeholder: __("Search for a patron"),
107
    });
110
    });
108
111
109
    $('#booking_patron_id').on('select2:select', function (e) {
112
    $("#booking_patron_id").on("select2:select", function (e) {
110
        booking_patron = e.params.data;
113
        booking_patron = e.params.data;
111
    });
114
    });
112
115
113
    // Adopt periodPicker
116
    // Adopt periodPicker
114
    let periodPicker = $("#period").get(0)._flatpickr;
117
    let periodPicker = $("#period").get(0)._flatpickr;
115
118
116
    if ( !dataFetched ) {
119
    if (!dataFetched) {
117
118
        // Fetch list of bookable items
120
        // Fetch list of bookable items
119
        let itemsFetch = $.ajax({
121
        let itemsFetch = $.ajax({
120
            url: '/api/v1/biblios/' + biblionumber + '/items?bookable=1' + '&_per_page=-1',
122
            url:
121
            dataType: 'json',
123
                "/api/v1/biblios/" +
122
            type: 'GET'
124
                biblionumber +
125
                "/items?bookable=1" +
126
                "&_per_page=-1",
127
            dataType: "json",
128
            type: "GET",
123
        });
129
        });
124
130
125
        // Fetch list of existing bookings
131
        // Fetch list of existing bookings
126
        let bookingsFetch = $.ajax({
132
        let bookingsFetch = $.ajax({
127
            url: '/api/v1/bookings?biblio_id=' + biblionumber + '&_per_page=-1',
133
            url: "/api/v1/bookings?biblio_id=" + biblionumber + "&_per_page=-1",
128
            dataType: 'json',
134
            dataType: "json",
129
            type: 'GET'
135
            type: "GET",
130
        });
136
        });
131
137
132
        // Fetch list of current checkouts
138
        // Fetch list of current checkouts
133
        let checkoutsFetch = $.ajax({
139
        let checkoutsFetch = $.ajax({
134
            url: '/api/v1/biblios/' + biblionumber + '/checkouts?_per_page=-1',
140
            url: "/api/v1/biblios/" + biblionumber + "/checkouts?_per_page=-1",
135
            dataType: 'json',
141
            dataType: "json",
136
            type: 'GET'
142
            type: "GET",
137
        });
143
        });
138
144
139
        // Update item select2 and period flatpickr
145
        // Update item select2 and period flatpickr
140
        $.when(itemsFetch, bookingsFetch, checkoutsFetch).then(
146
        $.when(itemsFetch, bookingsFetch, checkoutsFetch).then(
141
            function(itemsFetch,bookingsFetch, checkoutsFetch){
147
            function (itemsFetch, bookingsFetch, checkoutsFetch) {
142
143
                // Set variables
148
                // Set variables
144
                bookable_items = itemsFetch[0];
149
                bookable_items = itemsFetch[0];
145
                bookings = bookingsFetch[0];
150
                bookings = bookingsFetch[0];
Lines 161-174 $('#placeBookingModal').on('show.bs.modal', function(e) { Link Here
161
                // Item select2
166
                // Item select2
162
                $("#booking_item_id").select2({
167
                $("#booking_item_id").select2({
163
                    dropdownParent: $(".modal-content", "#placeBookingModal"),
168
                    dropdownParent: $(".modal-content", "#placeBookingModal"),
164
                    width: '50%',
169
                    width: "50%",
165
                    dropdownAutoWidth: true,
170
                    dropdownAutoWidth: true,
166
                    minimumResultsForSearch: 20,
171
                    minimumResultsForSearch: 20,
167
                    placeholder: __("Select item")
172
                    placeholder: __("Select item"),
168
                });
173
                });
169
174
170
                // Update flatpickr mode
175
                // Update flatpickr mode
171
                periodPicker.set('mode', 'range');
176
                periodPicker.set("mode", "range");
172
177
173
                // Total bookable items
178
                // Total bookable items
174
                let bookable = 0;
179
                let bookable = 0;
Lines 176-474 $('#placeBookingModal').on('show.bs.modal', function(e) { Link Here
176
                for (item of bookable_items) {
181
                for (item of bookable_items) {
177
                    bookable++;
182
                    bookable++;
178
                    // Populate item select (NOTE: Do we still need this check for pre-existing select option here?)
183
                    // Populate item select (NOTE: Do we still need this check for pre-existing select option here?)
179
                    if (!($('#booking_item_id').find("option[value='" + item.item_id + "']").length)) {
184
                    if (
185
                        !$("#booking_item_id").find(
186
                            "option[value='" + item.item_id + "']",
187
                        ).length
188
                    ) {
180
                        // Create a DOM Option and de-select by default
189
                        // Create a DOM Option and de-select by default
181
                        let newOption = new Option(escape_str(item.external_id), item.item_id, false, false);
190
                        let newOption = new Option(
191
                            escape_str(item.external_id),
192
                            item.item_id,
193
                            false,
194
                            false,
195
                        );
182
                        // Append it to the select
196
                        // Append it to the select
183
                        $('#booking_item_id').append(newOption);
197
                        $("#booking_item_id").append(newOption);
184
                    }
198
                    }
185
                }
199
                }
186
200
187
                // Set disable function for periodPicker
201
                // Set disable function for periodPicker
188
                let disableExists = periodPicker.config.disable.filter(f => f.name === 'dateDisable');
202
                let disableExists = periodPicker.config.disable.filter(
189
                if ( disableExists.length === 0 ) {
203
                    (f) => f.name === "dateDisable",
190
                    periodPicker.config.disable.push(function dateDisable(date){
204
                );
191
205
                if (disableExists.length === 0) {
192
                        // set local copy of selectedDates
206
                    periodPicker.config.disable.push(
193
                        let selectedDates = periodPicker.selectedDates;
207
                        function dateDisable(date) {
194
208
                            // set local copy of selectedDates
195
                        // set booked counter
209
                            let selectedDates = periodPicker.selectedDates;
196
                        let booked = 0;
210
197
211
                            // set booked counter
198
                        // reset the unavailable items array
212
                            let booked = 0;
199
                        let unavailable_items = [];
213
200
214
                            // reset the unavailable items array
201
                        // reset the biblio level bookings array
215
                            let unavailable_items = [];
202
                        let biblio_bookings = [];
216
203
217
                            // reset the biblio level bookings array
204
                        // disable dates before selected date
218
                            let biblio_bookings = [];
205
                        if (!selectedDates[1] && (selectedDates[0] && selectedDates[0] > date)) {
219
206
                            return true;
220
                            // disable dates before selected date
207
                        }
221
                            if (
208
222
                                !selectedDates[1] &&
209
                        // iterate existing bookings
223
                                selectedDates[0] &&
210
                        for (booking of bookings) {
224
                                selectedDates[0] > date
211
225
                            ) {
212
                            // Skip if we're editing this booking
226
                                return true;
213
                            if (booking_id && booking_id == booking.booking_id){
214
                                continue;
215
                            }
227
                            }
216
228
217
                            let start_date = flatpickr.parseDate(booking.start_date);
229
                            // iterate existing bookings
218
                            let end_date = flatpickr.parseDate(booking.end_date);
230
                            for (booking of bookings) {
219
231
                                // Skip if we're editing this booking
220
                            // patron has selected a start date (end date checks)
232
                                if (
221
                            if (selectedDates[0]) {
233
                                    booking_id &&
234
                                    booking_id == booking.booking_id
235
                                ) {
236
                                    continue;
237
                                }
222
238
223
                                // new booking start date is between existing booking start and end dates
239
                                let start_date = flatpickr.parseDate(
224
                                if (selectedDates[0] >= start_date && selectedDates[0] <= end_date) {
240
                                    booking.start_date,
225
                                    if (booking.item_id) {
241
                                );
226
                                        if (unavailable_items.indexOf(booking.item_id) === -1) {
242
                                let end_date = flatpickr.parseDate(
227
                                            unavailable_items.push(booking.item_id);
243
                                    booking.end_date,
228
                                        }
244
                                );
229
                                    } else {
245
230
                                        if (biblio_bookings.indexOf(booking.booking_id) === -1) {
246
                                // patron has selected a start date (end date checks)
231
                                            biblio_bookings.push(booking.booking_id);
247
                                if (selectedDates[0]) {
248
                                    // new booking start date is between existing booking start and end dates
249
                                    if (
250
                                        selectedDates[0] >= start_date &&
251
                                        selectedDates[0] <= end_date
252
                                    ) {
253
                                        if (booking.item_id) {
254
                                            if (
255
                                                unavailable_items.indexOf(
256
                                                    booking.item_id,
257
                                                ) === -1
258
                                            ) {
259
                                                unavailable_items.push(
260
                                                    booking.item_id,
261
                                                );
262
                                            }
263
                                        } else {
264
                                            if (
265
                                                biblio_bookings.indexOf(
266
                                                    booking.booking_id,
267
                                                ) === -1
268
                                            ) {
269
                                                biblio_bookings.push(
270
                                                    booking.booking_id,
271
                                                );
272
                                            }
232
                                        }
273
                                        }
233
                                    }
274
                                    }
234
                                }
235
275
236
                                // new booking end date would be between existing booking start and end dates
276
                                    // new booking end date would be between existing booking start and end dates
237
                                else if (date >= start_date && date <= end_date) {
277
                                    else if (
238
                                    if (booking.item_id) {
278
                                        date >= start_date &&
239
                                        if (unavailable_items.indexOf(booking.item_id) === -1) {
279
                                        date <= end_date
240
                                            unavailable_items.push(booking.item_id);
280
                                    ) {
241
                                        }
281
                                        if (booking.item_id) {
242
                                    } else {
282
                                            if (
243
                                        if (biblio_bookings.indexOf(booking.booking_id) === -1) {
283
                                                unavailable_items.indexOf(
244
                                            biblio_bookings.push(booking.booking_id);
284
                                                    booking.item_id,
285
                                                ) === -1
286
                                            ) {
287
                                                unavailable_items.push(
288
                                                    booking.item_id,
289
                                                );
290
                                            }
291
                                        } else {
292
                                            if (
293
                                                biblio_bookings.indexOf(
294
                                                    booking.booking_id,
295
                                                ) === -1
296
                                            ) {
297
                                                biblio_bookings.push(
298
                                                    booking.booking_id,
299
                                                );
300
                                            }
245
                                        }
301
                                        }
246
                                    }
302
                                    }
247
                                }
248
303
249
                                // new booking would span existing booking
304
                                    // new booking would span existing booking
250
                                else if (selectedDates[0] <= start_date && date >= end_date) {
305
                                    else if (
251
                                    if (booking.item_id) {
306
                                        selectedDates[0] <= start_date &&
252
                                        if (unavailable_items.indexOf(booking.item_id) === -1) {
307
                                        date >= end_date
253
                                            unavailable_items.push(booking.item_id);
308
                                    ) {
254
                                        }
309
                                        if (booking.item_id) {
255
                                    } else {
310
                                            if (
256
                                        if (biblio_bookings.indexOf(booking.booking_id) === -1) {
311
                                                unavailable_items.indexOf(
257
                                            biblio_bookings.push(booking.booking_id);
312
                                                    booking.item_id,
313
                                                ) === -1
314
                                            ) {
315
                                                unavailable_items.push(
316
                                                    booking.item_id,
317
                                                );
318
                                            }
319
                                        } else {
320
                                            if (
321
                                                biblio_bookings.indexOf(
322
                                                    booking.booking_id,
323
                                                ) === -1
324
                                            ) {
325
                                                biblio_bookings.push(
326
                                                    booking.booking_id,
327
                                                );
328
                                            }
258
                                        }
329
                                        }
259
                                    }
330
                                    }
260
                                }
261
331
262
                                // new booking would not conflict
332
                                    // new booking would not conflict
263
                                else {
333
                                    else {
264
                                    continue;
334
                                        continue;
265
                                }
335
                                    }
266
336
267
                                // check that there are available items
337
                                    // check that there are available items
268
                                // available = all bookable items - booked items - booked biblios
338
                                    // available = all bookable items - booked items - booked biblios
269
                                let total_available = bookable_items.length - unavailable_items.length - biblio_bookings.length;
339
                                    let total_available =
270
                                if (total_available === 0) {
340
                                        bookable_items.length -
271
                                    return true;
341
                                        unavailable_items.length -
342
                                        biblio_bookings.length;
343
                                    if (total_available === 0) {
344
                                        return true;
345
                                    }
272
                                }
346
                                }
273
                            }
274
347
275
                            // patron has not yet selected a start date (start date checks)
348
                                // patron has not yet selected a start date (start date checks)
276
                            else if (date <= end_date && date >= start_date) {
349
                                else if (
350
                                    date <= end_date &&
351
                                    date >= start_date
352
                                ) {
353
                                    // same item, disable date
354
                                    if (
355
                                        booking.item_id &&
356
                                        booking.item_id == booking_item_id
357
                                    ) {
358
                                        return true;
359
                                    }
277
360
278
                                // same item, disable date
361
                                    // count all clashes, both item and biblio level
279
                                if (booking.item_id && booking.item_id == booking_item_id) {
362
                                    booked++;
280
                                    return true;
363
                                    if (booked == bookable) {
281
                                }
364
                                        return true;
365
                                    }
282
366
283
                                // count all clashes, both item and biblio level
367
                                    // FIXME: The above is not intelligent enough to spot
284
                                booked++;
368
                                    // cases where an item must be used for a biblio level booking
285
                                if (booked == bookable) {
369
                                    // due to all other items being booking within the biblio level
286
                                    return true;
370
                                    // booking period... we end up with a clash
371
                                    // To reproduce:
372
                                    // * One bib with two bookable items.
373
                                    // * Add item level booking
374
                                    // * Add biblio level booking that extends one day beyond the item level booking
375
                                    // * Try to book the item without an item level booking from the day before the biblio level
376
                                    //   booking is to be returned. Note this is a clash, the only item available for the biblio
377
                                    //   level booking is the item you just booked out overlapping the end date.
287
                                }
378
                                }
288
289
                                // FIXME: The above is not intelligent enough to spot
290
                                // cases where an item must be used for a biblio level booking
291
                                // due to all other items being booking within the biblio level
292
                                // booking period... we end up with a clash
293
                                // To reproduce: 
294
                                // * One bib with two bookable items.
295
                                // * Add item level booking
296
                                // * Add biblio level booking that extends one day beyond the item level booking
297
                                // * Try to book the item without an item level booking from the day before the biblio level
298
                                //   booking is to be returned. Note this is a clash, the only item available for the biblio
299
                                //   level booking is the item you just booked out overlapping the end date.
300
                            }
379
                            }
301
                        }
380
                        },
302
                    });
381
                    );
303
                };
382
                }
304
383
305
                // Setup listener for item select2
384
                // Setup listener for item select2
306
                $('#booking_item_id').on('select2:select', function(e) {
385
                $("#booking_item_id").on("select2:select", function (e) {
307
                    booking_item_id = e.params.data.id ? e.params.data.id : null;
386
                    booking_item_id = e.params.data.id
387
                        ? e.params.data.id
388
                        : null;
308
389
309
                    // redraw pariodPicker taking selected item into account
390
                    // redraw pariodPicker taking selected item into account
310
                    periodPicker.redraw();
391
                    periodPicker.redraw();
311
                });
392
                });
312
393
313
                // Set onChange for flatpickr
394
                // Set onChange for flatpickr
314
                let changeExists = periodPicker.config.onChange.filter(f => f.name ==='periodChange');
395
                let changeExists = periodPicker.config.onChange.filter(
315
                if(changeExists.length === 0) {
396
                    (f) => f.name === "periodChange",
316
                    periodPicker.config.onChange.push(function periodChange(selectedDates, dateStr, instance) {
397
                );
317
                        // Range set, update hidden fields and set available items
398
                if (changeExists.length === 0) {
318
                        if ( selectedDates[0] && selectedDates[1] ) {
399
                    periodPicker.config.onChange.push(
319
                            // set form fields from picker
400
                        function periodChange(
320
                            let picker_start = dayjs(selectedDates[0]);
401
                            selectedDates,
321
                            let picker_end = dayjs(selectedDates[1]).endOf('day');
402
                            dateStr,
322
                            $('#booking_start_date').val(picker_start.toISOString());
403
                            instance,
323
                            $('#booking_end_date').val(picker_end.toISOString());
404
                        ) {
324
405
                            // Range set, update hidden fields and set available items
325
                            // set available items in select2
406
                            if (selectedDates[0] && selectedDates[1]) {
326
                            let booked_items = bookings.filter(function(booking) {
407
                                // set form fields from picker
327
                                let start_date = flatpickr.parseDate(booking.start_date);
408
                                let picker_start = dayjs(selectedDates[0]);
328
                                let end_date = flatpickr.parseDate(booking.end_date);
409
                                let picker_end = dayjs(selectedDates[1]).endOf(
329
                                // This booking ends before the start of the new booking
410
                                    "day",
330
                                if ( end_date <= selectedDates[0] ) {
411
                                );
331
                                    return false;
412
                                $("#booking_start_date").val(
332
                                }
413
                                    picker_start.toISOString(),
333
                                // This booking starts after then end of the new booking
414
                                );
334
                                if ( start_date >= selectedDates[1] ) {
415
                                $("#booking_end_date").val(
335
                                    return false;
416
                                    picker_end.toISOString(),
336
                                }
417
                                );
337
                                // This booking overlaps
418
338
                                return true;
419
                                // set available items in select2
339
                            });
420
                                let booked_items = bookings.filter(
340
                            $("#booking_item_id > option").each(function() {
421
                                    function (booking) {
341
                                let option = $(this);
422
                                        let start_date = flatpickr.parseDate(
342
                                if ( booking_item_id && booking_item_id == option.val() ) {
423
                                            booking.start_date,
343
                                    option.prop('disabled',false);
424
                                        );
344
                                } else if ( booked_items.some(function(booked_item){
425
                                        let end_date = flatpickr.parseDate(
345
                                    return option.val() == booked_item.item_id;
426
                                            booking.end_date,
346
                                }) ) {
427
                                        );
347
                                    option.prop('disabled',true);
428
                                        // This booking ends before the start of the new booking
348
                                } else {
429
                                        if (end_date <= selectedDates[0]) {
349
                                    option.prop('disabled',false);
430
                                            return false;
350
                                }
431
                                        }
351
                            });
432
                                        // This booking starts after then end of the new booking
352
                            $('#booking_item_id').trigger('change.select2');
433
                                        if (start_date >= selectedDates[1]) {
353
                        }
434
                                            return false;
354
                        // Range not set, reset field options
435
                                        }
355
                        else {
436
                                        // This booking overlaps
356
                            $('#booking_item_id > option').each(function() {
437
                                        return true;
357
                                $(this).prop('disabled', false);
438
                                    },
358
                            });
439
                                );
359
                            $('#booking_item_id').trigger('change.select2');
440
                                $("#booking_item_id > option").each(
360
                        }
441
                                    function () {
361
                    });
442
                                        let option = $(this);
362
                };
443
                                        if (
444
                                            booking_item_id &&
445
                                            booking_item_id == option.val()
446
                                        ) {
447
                                            option.prop("disabled", false);
448
                                        } else if (
449
                                            booked_items.some(
450
                                                function (booked_item) {
451
                                                    return (
452
                                                        option.val() ==
453
                                                        booked_item.item_id
454
                                                    );
455
                                                },
456
                                            )
457
                                        ) {
458
                                            option.prop("disabled", true);
459
                                        } else {
460
                                            option.prop("disabled", false);
461
                                        }
462
                                    },
463
                                );
464
                                $("#booking_item_id").trigger("change.select2");
465
                            }
466
                            // Range not set, reset field options
467
                            else {
468
                                $("#booking_item_id > option").each(
469
                                    function () {
470
                                        $(this).prop("disabled", false);
471
                                    },
472
                                );
473
                                $("#booking_item_id").trigger("change.select2");
474
                            }
475
                        },
476
                    );
477
                }
363
478
364
                // Enable flatpickr now we have date function populated
479
                // Enable flatpickr now we have date function populated
365
                periodPicker.redraw();
480
                periodPicker.redraw();
366
                $("#period_fields :input").prop('disabled', false);
481
                $("#period_fields :input").prop("disabled", false);
367
482
368
                // Redraw select with new options and enable
483
                // Redraw select with new options and enable
369
                $('#booking_item_id').trigger('change');
484
                $("#booking_item_id").trigger("change");
370
                $("#booking_item_id").prop("disabled", false);
485
                $("#booking_item_id").prop("disabled", false);
371
486
372
                // Set the flag to indicate that data has been fetched
487
                // Set the flag to indicate that data has been fetched
373
                dataFetched = true;
488
                dataFetched = true;
374
489
375
                // Set form values
490
                // Set form values
376
                setFormValues(patron_id,booking_item_id,start_date,end_date,periodPicker);
491
                setFormValues(
492
                    patron_id,
493
                    booking_item_id,
494
                    start_date,
495
                    end_date,
496
                    periodPicker,
497
                );
377
            },
498
            },
378
            function(jqXHR, textStatus, errorThrown){
499
            function (jqXHR, textStatus, errorThrown) {
379
                console.log("Fetch failed");
500
                console.log("Fetch failed");
380
            }
501
            },
381
        );
502
        );
382
    } else {
503
    } else {
383
        setFormValues(patron_id,booking_item_id,start_date,end_date,periodPicker);
504
        setFormValues(
384
    };
505
            patron_id,
506
            booking_item_id,
507
            start_date,
508
            end_date,
509
            periodPicker,
510
        );
511
    }
385
});
512
});
386
513
387
function setFormValues(patron_id,booking_item_id,start_date,end_date,periodPicker){
514
function setFormValues(
388
515
    patron_id,
516
    booking_item_id,
517
    start_date,
518
    end_date,
519
    periodPicker,
520
) {
389
    // If passed patron, pre-select
521
    // If passed patron, pre-select
390
    if (patron_id) {
522
    if (patron_id) {
391
        let patronSelect = $('#booking_patron_id');
523
        let patronSelect = $("#booking_patron_id");
392
        let patron = $.ajax({
524
        let patron = $.ajax({
393
            url: '/api/v1/patrons/' + patron_id,
525
            url: "/api/v1/patrons/" + patron_id,
394
            dataType: 'json',
526
            dataType: "json",
395
            type: 'GET'
527
            type: "GET",
396
        });
528
        });
397
529
398
        $.when(patron).done(
530
        $.when(patron).done(function (patron) {
399
            function(patron){
531
            // clone patron_id to id (select2 expects an id field)
400
532
            patron.id = patron.patron_id;
401
                // clone patron_id to id (select2 expects an id field)
533
            patron.text =
402
                patron.id = patron.patron_id;
534
                escape_str(patron.surname) +
403
                patron.text = escape_str(patron.surname) + ", " + escape_str(patron.firstname);
535
                ", " +
404
536
                escape_str(patron.firstname);
405
                // Add and select new option
537
406
                let newOption = new Option(patron.text, patron.id, true, true);
538
            // Add and select new option
407
                patronSelect.append(newOption).trigger('change');
539
            let newOption = new Option(patron.text, patron.id, true, true);
408
540
            patronSelect.append(newOption).trigger("change");
409
                // manually trigger the `select2:select` event
541
410
                patronSelect.trigger({
542
            // manually trigger the `select2:select` event
411
                    type: 'select2:select',
543
            patronSelect.trigger({
412
                    params: {
544
                type: "select2:select",
413
                        data: patron
545
                params: {
414
                    }
546
                    data: patron,
415
                });
547
                },
416
            }
548
            });
417
        );
549
        });
418
    }
550
    }
419
551
420
    // Set booking start & end if this is an edit
552
    // Set booking start & end if this is an edit
421
    if ( start_date ) {
553
    if (start_date) {
422
        // Allow invalid pre-load so setDate can set date range
554
        // Allow invalid pre-load so setDate can set date range
423
        // periodPicker.set('allowInvalidPreload', true);
555
        // periodPicker.set('allowInvalidPreload', true);
424
        // FIXME: Why is this the case.. we're passing two valid Date objects
556
        // FIXME: Why is this the case.. we're passing two valid Date objects
425
        let start = new Date(start_date);
557
        let start = new Date(start_date);
426
        let end = new Date(end_date);
558
        let end = new Date(end_date);
427
559
428
        let dates = [ new Date(start_date), new Date(end_date) ];
560
        let dates = [new Date(start_date), new Date(end_date)];
429
        periodPicker.setDate(dates, true);
561
        periodPicker.setDate(dates, true);
430
    }
562
    }
431
    // Reset periodPicker, biblio_id may have been nulled
563
    // Reset periodPicker, biblio_id may have been nulled
432
    else {
564
    else {
433
        periodPicker.redraw();
565
        periodPicker.redraw();
434
    };
566
    }
435
567
436
    // If passed an itemnumber, pre-select
568
    // If passed an itemnumber, pre-select
437
    if (booking_item_id) {
569
    if (booking_item_id) {
438
        $('#booking_item_id').val(booking_item_id).trigger('change');
570
        $("#booking_item_id").val(booking_item_id).trigger("change");
439
    }
571
    }
440
}
572
}
441
573
442
$("#placeBookingForm").on('submit', function(e) {
574
$("#placeBookingForm").on("submit", function (e) {
443
    e.preventDefault();
575
    e.preventDefault();
444
576
445
    let url = '/api/v1/bookings';
577
    let url = "/api/v1/bookings";
446
578
447
    let start_date = $('#booking_start_date').val();
579
    let start_date = $("#booking_start_date").val();
448
    let end_date = $('#booking_end_date').val();
580
    let end_date = $("#booking_end_date").val();
449
    let item_id = $('#booking_item_id').val();
581
    let item_id = $("#booking_item_id").val();
450
582
451
    if (!booking_id) {
583
    if (!booking_id) {
452
        let posting = $.post(
584
        let posting = $.post(
453
            url,
585
            url,
454
            JSON.stringify({
586
            JSON.stringify({
455
                "start_date": start_date,
587
                start_date: start_date,
456
                "end_date": end_date,
588
                end_date: end_date,
457
                "biblio_id": $('#booking_biblio_id').val(),
589
                biblio_id: $("#booking_biblio_id").val(),
458
                "item_id": item_id != 0 ? item_id : null,
590
                item_id: item_id != 0 ? item_id : null,
459
                "patron_id": $('#booking_patron_id').find(':selected').val()
591
                patron_id: $("#booking_patron_id").find(":selected").val(),
460
            })
592
            }),
461
        );
593
        );
462
594
463
        posting.done(function(data) {
595
        posting.done(function (data) {
464
            // Update bookings store for subsequent bookings
596
            // Update bookings store for subsequent bookings
465
            bookings.push(data);
597
            bookings.push(data);
466
598
467
            // Update bookings page as required
599
            // Update bookings page as required
468
            if (typeof bookings_table !== 'undefined' && bookings_table !== null) {
600
            if (
601
                typeof bookings_table !== "undefined" &&
602
                bookings_table !== null
603
            ) {
469
                bookings_table.api().ajax.reload();
604
                bookings_table.api().ajax.reload();
470
            }
605
            }
471
            if (typeof timeline !== 'undefined' && timeline !== null) {
606
            if (typeof timeline !== "undefined" && timeline !== null) {
472
                timeline.itemsData.add({
607
                timeline.itemsData.add({
473
                    id: data.booking_id,
608
                    id: data.booking_id,
474
                    booking: data.booking_id,
609
                    booking: data.booking_id,
Lines 477-528 $("#placeBookingForm").on('submit', function(e) { Link Here
477
                    end: dayjs(data.end_date).toDate(),
612
                    end: dayjs(data.end_date).toDate(),
478
                    content: $patron_to_html(booking_patron, {
613
                    content: $patron_to_html(booking_patron, {
479
                        display_cardnumber: true,
614
                        display_cardnumber: true,
480
                        url: false
615
                        url: false,
481
                    }),
616
                    }),
482
                    editable: { remove: true, updateTime: true },
617
                    editable: { remove: true, updateTime: true },
483
                    type: 'range',
618
                    type: "range",
484
                    group: data.item_id ? data.item_id : 0
619
                    group: data.item_id ? data.item_id : 0,
485
                });
620
                });
486
                timeline.focus(data.booking_id);
621
                timeline.focus(data.booking_id);
487
            }
622
            }
488
623
489
            // Update bookings counts
624
            // Update bookings counts
490
            $('.bookings_count').html(parseInt($('.bookings_count').html(), 10)+1);
625
            $(".bookings_count").html(
626
                parseInt($(".bookings_count").html(), 10) + 1,
627
            );
491
628
492
            // Close modal
629
            // Close modal
493
            $('#placeBookingModal').modal('hide');
630
            $("#placeBookingModal").modal("hide");
494
        });
631
        });
495
632
496
        posting.fail(function(data) {
633
        posting.fail(function (data) {
497
            $('#booking_result').replaceWith('<div id="booking_result" class="alert alert-danger">'+_("Failure")+'</div>');
634
            $("#booking_result").replaceWith(
635
                '<div id="booking_result" class="alert alert-danger">' +
636
                    _("Failure") +
637
                    "</div>",
638
            );
498
        });
639
        });
499
    } else {
640
    } else {
500
        url += '/' + booking_id;
641
        url += "/" + booking_id;
501
        let putting = $.ajax({
642
        let putting = $.ajax({
502
            'method': 'PUT',
643
            method: "PUT",
503
            'url': url,
644
            url: url,
504
            'data': JSON.stringify({
645
            data: JSON.stringify({
505
                "booking_id": booking_id,
646
                booking_id: booking_id,
506
                "start_date": start_date,
647
                start_date: start_date,
507
                "end_date": end_date,
648
                end_date: end_date,
508
                "biblio_id": $('#booking_biblio_id').val(),
649
                biblio_id: $("#booking_biblio_id").val(),
509
                "item_id": item_id != 0 ? item_id : null,
650
                item_id: item_id != 0 ? item_id : null,
510
                "patron_id": $('#booking_patron_id').find(':selected').val()
651
                patron_id: $("#booking_patron_id").find(":selected").val(),
511
            })
652
            }),
512
        });
653
        });
513
654
514
        putting.done(function(data) {
655
        putting.done(function (data) {
515
            update_success = 1;
656
            update_success = 1;
516
657
517
            // Update bookings store for subsequent bookings
658
            // Update bookings store for subsequent bookings
518
            let target = bookings.find((obj) => obj.booking_id === data.booking_id);
659
            let target = bookings.find(
519
            Object.assign(target,data);
660
                (obj) => obj.booking_id === data.booking_id,
661
            );
662
            Object.assign(target, data);
520
663
521
            // Update bookings page as required
664
            // Update bookings page as required
522
            if (typeof bookings_table !== 'undefined' && bookings_table !== null) {
665
            if (
666
                typeof bookings_table !== "undefined" &&
667
                bookings_table !== null
668
            ) {
523
                bookings_table.api().ajax.reload();
669
                bookings_table.api().ajax.reload();
524
            }
670
            }
525
            if (typeof timeline !== 'undefined' && timeline !== null) {
671
            if (typeof timeline !== "undefined" && timeline !== null) {
526
                timeline.itemsData.update({
672
                timeline.itemsData.update({
527
                    id: data.booking_id,
673
                    id: data.booking_id,
528
                    booking: data.booking_id,
674
                    booking: data.booking_id,
Lines 531-561 $("#placeBookingForm").on('submit', function(e) { Link Here
531
                    end: dayjs(data.end_date).toDate(),
677
                    end: dayjs(data.end_date).toDate(),
532
                    content: $patron_to_html(booking_patron, {
678
                    content: $patron_to_html(booking_patron, {
533
                        display_cardnumber: true,
679
                        display_cardnumber: true,
534
                        url: false
680
                        url: false,
535
                    }),
681
                    }),
536
                    editable: { remove: true, updateTime: true },
682
                    editable: { remove: true, updateTime: true },
537
                    type: 'range',
683
                    type: "range",
538
                    group: data.item_id ? data.item_id : 0
684
                    group: data.item_id ? data.item_id : 0,
539
                });
685
                });
540
                timeline.focus(data.booking_id);
686
                timeline.focus(data.booking_id);
541
            }
687
            }
542
688
543
            // Close modal
689
            // Close modal
544
            $('#placeBookingModal').modal('hide');
690
            $("#placeBookingModal").modal("hide");
545
        });
691
        });
546
692
547
        putting.fail(function(data) {
693
        putting.fail(function (data) {
548
            $('#booking_result').replaceWith('<div id="booking_result" class="alert alert-danger">'+__("Failure")+'</div>');
694
            $("#booking_result").replaceWith(
695
                '<div id="booking_result" class="alert alert-danger">' +
696
                    __("Failure") +
697
                    "</div>",
698
            );
549
        });
699
        });
550
    }
700
    }
551
});
701
});
552
702
553
$('#placeBookingModal').on('hidden.bs.modal', function (e) {
703
$("#placeBookingModal").on("hidden.bs.modal", function (e) {
554
    $('#booking_patron_id').val(null).trigger('change');
704
    $("#booking_patron_id").val(null).trigger("change");
555
    $('#booking_patron_id').empty();
705
    $("#booking_patron_id").empty();
556
    $('#booking_item_id').val(0).trigger('change');
706
    $("#booking_item_id").val(0).trigger("change");
557
    $('#period').get(0)._flatpickr.clear();
707
    $("#period").get(0)._flatpickr.clear();
558
    $('#booking_start_date').val('');
708
    $("#booking_start_date").val("");
559
    $('#booking_end_date').val('');
709
    $("#booking_end_date").val("");
560
    $('#booking_id').val('');
710
    $("#booking_id").val("");
561
});
711
});
562
- 

Return to bug 36374