Bug 30650: Be more flexible with opening slots

Sponsored-by: Association KohaLa - https://koha-fr.org/

Signed-off-by: Koha Team University Lyon 3 <koha@univ-lyon3.fr>

Signed-off-by: Katrin Fischer <katrin.fischer@bsz-bw.de>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
This commit is contained in:
Jonathan Druart 2022-05-02 15:21:27 +02:00 committed by Tomas Cohen Arazi
parent 08ad52b5e9
commit 197ad75727
Signed by: tomascohen
GPG key ID: 0A272EA1B2F3C15F
5 changed files with 169 additions and 77 deletions

View file

@ -0,0 +1,44 @@
package Koha::CurbsidePickupOpeningSlot;
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with Koha; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
use Modern::Perl;
use Carp;
use Koha::Database;
use base qw(Koha::Object);
=head1 NAME
CurbsidePickupOpeningSlot - Koha Curbside Pickup Slot Object class
=head1 API
=head2 Class methods
=head2 Internal methods
=head3 _type
=cut
sub _type {
return 'CurbsidePickupOpeningSlot';
}
1;

View file

@ -0,0 +1,50 @@
package Koha::CurbsidePickupOpeningSlots;
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with Koha; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
use Modern::Perl;
use Carp;
use Koha::Database;
use Koha::CurbsidePickupOpeningSlot;
use base qw(Koha::Objects);
=head1 NAME
Koha::CurbsidePickupOpeningSlots - Koha Curbside Pickup Opening Slots Object set class
=head1 API
=head2 Class Methods
=cut
=head3 type
=cut
sub _type {
return 'CurbsidePickupOpeningSlot';
}
sub object_class {
return 'Koha::CurbsidePickupOpeningSlot';
}
1;

View file

@ -20,6 +20,8 @@ use Modern::Perl;
use Carp; use Carp;
use Koha::Database; use Koha::Database;
use Koha::Library;
use Koha::CurbsidePickupOpeningSlots;
use base qw(Koha::Object); use base qw(Koha::Object);
@ -44,6 +46,32 @@ sub library {
return Koha::Library->_new_from_dbic( $rs ); return Koha::Library->_new_from_dbic( $rs );
} }
sub opening_slots {
my ( $self ) = @_;
my $rs = $self->_result->curbside_pickup_opening_slots;
return unless $rs;
return Koha::CurbsidePickupOpeningSlots->_new_from_dbic( $rs );
}
sub add_opening_slot {
my ( $self, $slot ) = @_;
my ( $day, $start, $end ) = split '-', $slot;
my ( $start_hour, $start_minute ) = split ':', $start;
my ( $end_hour, $end_minute ) = split ':', $end;
return Koha::CurbsidePickupOpeningSlot->new(
{
curbside_pickup_policy_id => $self->id,
day => $day,
start_hour => $start_hour,
start_minute => $start_minute,
end_hour => $end_hour,
end_minute => $end_minute,
}
)->store;
}
=head2 Internal methods =head2 Internal methods
=head3 _type =head3 _type

View file

@ -43,7 +43,6 @@ if ( $op eq 'save' ) {
my $branchcode = $library->branchcode; my $branchcode = $library->branchcode;
my $params = { my $params = {
branchcode => $branchcode, branchcode => $branchcode,
enabled => scalar $input->param("enable-$branchcode") || 0, enabled => scalar $input->param("enable-$branchcode") || 0,
pickup_interval => scalar $input->param("interval-$branchcode"), pickup_interval => scalar $input->param("interval-$branchcode"),
@ -51,29 +50,16 @@ if ( $op eq 'save' ) {
patron_scheduled_pickup => scalar $input->param("patron-scheduled-$branchcode") || 0, patron_scheduled_pickup => scalar $input->param("patron-scheduled-$branchcode") || 0,
}; };
for my $day ( my $policy =
qw( sunday monday tuesday wednesday thursday friday saturday )) Koha::CurbsidePickupPolicies->find_or_create( { branchcode => $branchcode } );
{ $policy->update($params);
for my $start_end (qw( start end )) {
for my $hour_min (qw( hour minute )) {
my $value = $input->param( $policy->opening_slots->delete;
"pickup-$start_end-$hour_min-$day-$branchcode"); my @pickup_slots = $input->multi_param("pickup-slot-" . $branchcode);
$value = undef if $value eq q{}; for my $pickup_slot ( @pickup_slots ) {
$policy->add_opening_slot($pickup_slot);
my $key = $day . '_' . $start_end . '_' . $hour_min;
$params->{$key} = $value;
} }
} }
}
my $CurbsidePickupPolicy =
Koha::CurbsidePickupPolicies->find( { branchcode => $branchcode } );
$CurbsidePickupPolicy->delete if $CurbsidePickupPolicy;
Koha::CurbsidePickupPolicy->new($params)->store();
}
$op = 'list'; $op = 'list';
} }

View file

@ -9,8 +9,9 @@
[% INCLUDE 'doc-head-close.inc' %] [% INCLUDE 'doc-head-close.inc' %]
<style> <style>
.pickup-slot { .pickup-slot {
border: solid; border: 2px solid #b9d8d9;
padding: 1em; padding: 0 .1em;
margin: 0 .1em;
} }
</style> </style>
</head> </head>
@ -119,29 +120,7 @@
<em>Times should be in 24-hour format (00:00 to 23:59).</em> <em>Times should be in 24-hour format (00:00 to 23:59).</em>
<ol class="pickup_hours"> <ol class="pickup_hours"></ol>
[% BLOCK pickup_hours_day %]
[% SET day_start_hour = d _ '_start_hour' %]
[% SET day_start_minute = d _ '_start_minute' %]
[% SET day_end_hour = d _ '_end_hour' %]
[% SET day_end_minute = d _ '_end_minute' %]
[% SET p = policies.$branchcode %]
[% IF p.$day_start_hour && p.$day_start_minute && p.$day_end_hour && p.$day_end_minute %]
<li>
<span class="slot">
<input type="hidden" class="pickup-slot" name="pickup-slot-[% l.branchcode | html %]" value="[% d | html %]-[% p.$day_start_hour %]:[% p.$day_start_minute %]-[% p.$day_end_hour %]:[% p.$day_end_minute %]" />
</span>
</li>
[% END %]
[% END %]
[% PROCESS pickup_hours_day d => 'sunday' %]
[% PROCESS pickup_hours_day d => 'monday' %]
[% PROCESS pickup_hours_day d => 'tuesday' %]
[% PROCESS pickup_hours_day d => 'wednesday' %]
[% PROCESS pickup_hours_day d => 'thursday' %]
[% PROCESS pickup_hours_day d => 'friday' %]
[% PROCESS pickup_hours_day d => 'saturday' %]
</ol>
<ol> <ol>
<li> <li>
@ -161,6 +140,7 @@
</span> </span>
</select> </select>
<input type="button" class="add-new-slot" data-branchcode="[% l.branchcode | html %]" value="Add" /> <input type="button" class="add-new-slot" data-branchcode="[% l.branchcode | html %]" value="Add" />
<span id="invalid_slot_warning" style="display:none;">Invalid format for this new slot, must be '00:00 to 23:59'.</span>
</div> </div>
</li> </li>
@ -210,32 +190,41 @@
return String(hh).padStart(2, '0') + ':' + String(mm).padStart(2, '0'); return String(hh).padStart(2, '0') + ':' + String(mm).padStart(2, '0');
} }
function format_slot(slot){ function format_slot(slot){
let start = slot[0]; let day, start, end;
let end = slot[1]; [ day, start, end ] = slot.split("-");
return format_hhmm(start) + _(" to ") + format_hhmm(end); return format_hhmm(start) + _(" to ") + format_hhmm(end);
} }
function refresh_pickup_hours(branchcode) { function delete_slot(node, branchcode){
let slots = {}; let slot = $(node).find('.pickup-slot').val();
$("#conf-"+branchcode).find("input.pickup-slot").each(function(){ let splitted = slot.split("-");
let splitted = $(this).val().split("-");
let day = splitted[0]; let day = splitted[0];
let start = splitted[1]; let start = splitted[1];
let end = splitted[2]; let end = splitted[2];
opening_slots[branchcode].splice($.inArray(start+'-'+end, opening_slots), 1);
if(!slots[day]) slots[day] = []; refresh_pickup_hours(branchcode);
}
slots[day].push([start, end]); function refresh_pickup_hours(branchcode) {
let slots_per_day = {};
opening_slots[branchcode].forEach(function(slot){
let day, start, end;
[ day, start, end ] = slot.split("-");
if(!slots_per_day[day]) slots_per_day[day] = [];
slots_per_day[day].push(slot);
}); });
let new_slot_node = $("<span class='slot'><input type='hidden' class='pickup-slot' name='pickup-slot-"+branchcode+"' value='"+day+"-"+start+"-"+end+"'/></span>");
$("#conf-"+branchcode).find(".pickup_hours").append(new_slot_node);
$("#conf-"+branchcode).find(".pickup_hours li").remove(); $("#conf-"+branchcode).find(".pickup_hours li").remove();
Object.keys(slots).forEach(function(day){ Object.keys(slots_per_day).forEach(function(day){
let li_node = $('<li><label>'+get_day_lib(day)+'<label></li>'); let li_node = $('<li><label>'+get_day_lib(day)+'<label></li>');
slots[day].forEach(function(slot) { slots_per_day[day].forEach(function(slot) {
li_node.append('<span>'+format_slot(slot)+'</span>'); let span_node = $('<span class="pickup-slot"></span>');
span_node.append('<input type="hidden" class="pickup-slot" name="pickup-slot-'+branchcode+'" value="'+slot+'"/>');
span_node.append('<span>'+format_slot(slot)+'</span>');
let delete_link = $('<a href="#" on><i class="fa fa-trash" aria-hidden="true"></i>').on('click', function(e){ e.preventDefault; delete_slot($(this).closest('li'), branchcode); });
span_node.append(delete_link);
span_node.appendTo(li_node);
}); });
li_node.appendTo($("#conf-"+branchcode).find(".pickup_hours")); li_node.appendTo($("#conf-"+branchcode).find(".pickup_hours"));
}); });
@ -243,35 +232,31 @@
function get_day_lib(day){ function get_day_lib(day){
let lib; let lib;
switch(day){ switch(day){
case 'sunday': case '0':
lib = _("Sunday"); lib = _("Sunday");
break; break;
case 'monday': case '1':
lib = _("Monday"); lib = _("Monday");
break; break;
case 'tuesday': case '2':
lib = _("Tuesday"); lib = _("Tuesday");
break; break;
case 'wednesday': case '3':
lib = _("Wednesday"); lib = _("Wednesday");
break; break;
case 'thursday': case '4':
lib = _("Thursday"); lib = _("Thursday");
break; break;
case 'friday': case '5':
lib = _("Friday"); lib = _("Friday");
break; break;
case 'saturday': case '6':
lib = _("Saturday"); lib = _("Saturday");
break; break;
} }
return lib; return lib;
} }
let opening_slots = {};
[% FOR l IN libraries %]
opening_slots["[% l.branchcode | html %]"] = [];
[% END %]
$(document).ready(function(){ $(document).ready(function(){
[% FOR l IN libraries %] [% FOR l IN libraries %]
refresh_pickup_hours("[% l.branchcode | html %]"); refresh_pickup_hours("[% l.branchcode | html %]");
@ -282,7 +267,6 @@
let day = $("#day-" + branchcode).val(); let day = $("#day-" + branchcode).val();
let start = $("#new-start-" + branchcode).val(); let start = $("#new-start-" + branchcode).val();
let end = $("#new-end-" + branchcode).val(); let end = $("#new-end-" + branchcode).val();
// FIXME confirm selection
let start_hour, start_minute, end_hour, end_minute; let start_hour, start_minute, end_hour, end_minute;
[ start_hour, start_minute ] = start.split(":"); [ start_hour, start_minute ] = start.split(":");