@@ -, +, @@ + Added table encoding to atomic update + Added table definition to kohastructure.sql - Removed `repeatable_holidays`, `special_holidays` from kohastructure.sql + Added 'override' param to Koha::DiscreteCalendar --- C4/Calendar.pm | 713 ---------------- C4/Circulation.pm | 13 +- C4/HoldsQueue.pm | 4 +- C4/Overdues.pm | 4 +- C4/Reserves.pm | 2 +- Koha/DiscreteCalendar.pm | 64 +- Koha/Hold.pm | 4 +- circ/returns.pl | 2 +- .../bug_17015_part1_create_discrete_calendar.sql | 2 +- .../bug_17015_part2_fill_discrete_calendar.perl | 20 +- .../atomicupdate/bug_17015_part3_drop_calendar.sql | 5 + installer/data/mysql/kohastructure.sql | 49 +- installer/data/mysql/updatedatabase.pl | 2 +- .../intranet-tmpl/prog/css/discretecalendar.css | 206 +++++ .../prog/en/modules/tools/discrete_calendar.tt | 898 +++++++++------------ misc/cronjobs/add_days_discrete_calendar.pl | 3 +- misc/cronjobs/fines.pl | 2 +- misc/cronjobs/overdue_notices.pl | 6 +- misc/cronjobs/staticfines.pl | 2 +- .../thirdparty/TalkingTech_itiva_outbound.pl | 2 +- t/db_dependent/Circulation/CalcDateDue.t | 47 +- t/db_dependent/DiscreteCalendar.t | 44 +- t/db_dependent/Hold.t | 20 +- t/db_dependent/Holds.t | 1 - t/db_dependent/HoldsQueue.t | 12 +- tools/discrete_calendar.pl | 7 +- 26 files changed, 762 insertions(+), 1372 deletions(-) delete mode 100644 C4/Calendar.pm create mode 100644 installer/data/mysql/atomicupdate/bug_17015_part3_drop_calendar.sql create mode 100644 koha-tmpl/intranet-tmpl/prog/css/discretecalendar.css --- a/C4/Calendar.pm +++ a/C4/Calendar.pm @@ -1,713 +0,0 @@ -package C4::Calendar; - -# 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, see . - -use strict; -use warnings; -use vars qw(@EXPORT); - -use Carp; -use Date::Calc qw( Date_to_Days Today); - -use C4::Context; -use Koha::Caches; - -use constant ISO_DATE_FORMAT => "%04d-%02d-%02d"; - -=head1 NAME - -C4::Calendar::Calendar - Koha module dealing with holidays. - -=head1 SYNOPSIS - - use C4::Calendar::Calendar; - -=head1 DESCRIPTION - -This package is used to deal with holidays. Through this package, you can set -all kind of holidays for the library. - -=head1 FUNCTIONS - -=head2 new - - $calendar = C4::Calendar->new(branchcode => $branchcode); - -Each library branch has its own Calendar. -C<$branchcode> specifies which Calendar you want. - -=cut - -sub new { - my $classname = shift @_; - my %options = @_; - my $self = bless({}, $classname); - foreach my $optionName (keys %options) { - $self->{lc($optionName)} = $options{$optionName}; - } - defined($self->{branchcode}) or croak "No branchcode argument to new. Should be C4::Calendar->new(branchcode => \$branchcode)"; - $self->_init($self->{branchcode}); - return $self; -} - -sub _init { - my $self = shift @_; - my $branch = shift; - defined($branch) or die "No branchcode sent to _init"; # must test for defined here and above to allow "" - my $dbh = C4::Context->dbh(); - my $repeatable = $dbh->prepare( 'SELECT * - FROM repeatable_holidays - WHERE ( branchcode = ? ) - AND (ISNULL(weekday) = ?)' ); - $repeatable->execute($branch,0); - my %week_days_holidays; - while (my $row = $repeatable->fetchrow_hashref) { - my $key = $row->{weekday}; - $week_days_holidays{$key}{title} = $row->{title}; - $week_days_holidays{$key}{description} = $row->{description}; - } - $self->{'week_days_holidays'} = \%week_days_holidays; - - $repeatable->execute($branch,1); - my %day_month_holidays; - while (my $row = $repeatable->fetchrow_hashref) { - my $key = $row->{month} . "/" . $row->{day}; - $day_month_holidays{$key}{title} = $row->{title}; - $day_month_holidays{$key}{description} = $row->{description}; - $day_month_holidays{$key}{day} = sprintf("%02d", $row->{day}); - $day_month_holidays{$key}{month} = sprintf("%02d", $row->{month}); - } - $self->{'day_month_holidays'} = \%day_month_holidays; - - my $special = $dbh->prepare( 'SELECT day, month, year, title, description - FROM special_holidays - WHERE ( branchcode = ? ) - AND (isexception = ?)' ); - $special->execute($branch,1); - my %exception_holidays; - while (my ($day, $month, $year, $title, $description) = $special->fetchrow) { - $exception_holidays{"$year/$month/$day"}{title} = $title; - $exception_holidays{"$year/$month/$day"}{description} = $description; - $exception_holidays{"$year/$month/$day"}{date} = - sprintf(ISO_DATE_FORMAT, $year, $month, $day); - } - $self->{'exception_holidays'} = \%exception_holidays; - - $special->execute($branch,0); - my %single_holidays; - while (my ($day, $month, $year, $title, $description) = $special->fetchrow) { - $single_holidays{"$year/$month/$day"}{title} = $title; - $single_holidays{"$year/$month/$day"}{description} = $description; - $single_holidays{"$year/$month/$day"}{date} = - sprintf(ISO_DATE_FORMAT, $year, $month, $day); - } - $self->{'single_holidays'} = \%single_holidays; - return $self; -} - -=head2 get_week_days_holidays - - $week_days_holidays = $calendar->get_week_days_holidays(); - -Returns a hash reference to week days holidays. - -=cut - -sub get_week_days_holidays { - my $self = shift @_; - my $week_days_holidays = $self->{'week_days_holidays'}; - return $week_days_holidays; -} - -=head2 get_day_month_holidays - - $day_month_holidays = $calendar->get_day_month_holidays(); - -Returns a hash reference to day month holidays. - -=cut - -sub get_day_month_holidays { - my $self = shift @_; - my $day_month_holidays = $self->{'day_month_holidays'}; - return $day_month_holidays; -} - -=head2 get_exception_holidays - - $exception_holidays = $calendar->exception_holidays(); - -Returns a hash reference to exception holidays. This kind of days are those -which stands for a holiday, but you wanted to make an exception for this particular -date. - -=cut - -sub get_exception_holidays { - my $self = shift @_; - my $exception_holidays = $self->{'exception_holidays'}; - return $exception_holidays; -} - -=head2 get_single_holidays - - $single_holidays = $calendar->get_single_holidays(); - -Returns a hash reference to single holidays. This kind of holidays are those which -happened just one time. - -=cut - -sub get_single_holidays { - my $self = shift @_; - my $single_holidays = $self->{'single_holidays'}; - return $single_holidays; -} - -=head2 insert_week_day_holiday - - insert_week_day_holiday(weekday => $weekday, - title => $title, - description => $description); - -Inserts a new week day for $self->{branchcode}. - -C<$day> Is the week day to make holiday. - -C<$title> Is the title to store for the holiday formed by $year/$month/$day. - -C<$description> Is the description to store for the holiday formed by $year/$month/$day. - -=cut - -sub insert_week_day_holiday { - my $self = shift @_; - my %options = @_; - - my $weekday = $options{weekday}; - croak "Invalid weekday $weekday" unless $weekday =~ m/^[0-6]$/; - - my $dbh = C4::Context->dbh(); - my $insertHoliday = $dbh->prepare("insert into repeatable_holidays (branchcode,weekday,day,month,title,description) values ( ?,?,NULL,NULL,?,? )"); - $insertHoliday->execute( $self->{branchcode}, $weekday, $options{title}, $options{description}); - $self->{'week_days_holidays'}->{$weekday}{title} = $options{title}; - $self->{'week_days_holidays'}->{$weekday}{description} = $options{description}; - return $self; -} - -=head2 insert_day_month_holiday - - insert_day_month_holiday(day => $day, - month => $month, - title => $title, - description => $description); - -Inserts a new day month holiday for $self->{branchcode}. - -C<$day> Is the day month to make the date to insert. - -C<$month> Is month to make the date to insert. - -C<$title> Is the title to store for the holiday formed by $year/$month/$day. - -C<$description> Is the description to store for the holiday formed by $year/$month/$day. - -=cut - -sub insert_day_month_holiday { - my $self = shift @_; - my %options = @_; - - my $dbh = C4::Context->dbh(); - my $insertHoliday = $dbh->prepare("insert into repeatable_holidays (branchcode,weekday,day,month,title,description) values (?, NULL, ?, ?, ?,? )"); - $insertHoliday->execute( $self->{branchcode}, $options{day},$options{month},$options{title}, $options{description}); - $self->{'day_month_holidays'}->{"$options{month}/$options{day}"}{title} = $options{title}; - $self->{'day_month_holidays'}->{"$options{month}/$options{day}"}{description} = $options{description}; - return $self; -} - -=head2 insert_single_holiday - - insert_single_holiday(day => $day, - month => $month, - year => $year, - title => $title, - description => $description); - -Inserts a new single holiday for $self->{branchcode}. - -C<$day> Is the day month to make the date to insert. - -C<$month> Is month to make the date to insert. - -C<$year> Is year to make the date to insert. - -C<$title> Is the title to store for the holiday formed by $year/$month/$day. - -C<$description> Is the description to store for the holiday formed by $year/$month/$day. - -=cut - -sub insert_single_holiday { - my $self = shift @_; - my %options = @_; - @options{qw(year month day)} = ( $options{date} =~ m/(\d+)-(\d+)-(\d+)/o ) - if $options{date} && !$options{day}; - - my $dbh = C4::Context->dbh(); - my $isexception = 0; - my $insertHoliday = $dbh->prepare("insert into special_holidays (branchcode,day,month,year,isexception,title,description) values (?,?,?,?,?,?,?)"); - $insertHoliday->execute( $self->{branchcode}, $options{day},$options{month},$options{year}, $isexception, $options{title}, $options{description}); - $self->{'single_holidays'}->{"$options{year}/$options{month}/$options{day}"}{title} = $options{title}; - $self->{'single_holidays'}->{"$options{year}/$options{month}/$options{day}"}{description} = $options{description}; - - - # changed the 'single_holidays' table, lets force/reset its cache - my $cache = Koha::Caches->get_instance(); - $cache->clear_from_cache( 'single_holidays') ; - $cache->clear_from_cache( 'exception_holidays') ; - - return $self; - -} - -=head2 insert_exception_holiday - - insert_exception_holiday(day => $day, - month => $month, - year => $year, - title => $title, - description => $description); - -Inserts a new exception holiday for $self->{branchcode}. - -C<$day> Is the day month to make the date to insert. - -C<$month> Is month to make the date to insert. - -C<$year> Is year to make the date to insert. - -C<$title> Is the title to store for the holiday formed by $year/$month/$day. - -C<$description> Is the description to store for the holiday formed by $year/$month/$day. - -=cut - -sub insert_exception_holiday { - my $self = shift @_; - my %options = @_; - - @options{qw(year month day)} = ( $options{date} =~ m/(\d+)-(\d+)-(\d+)/o ) - if $options{date} && !$options{day}; - - my $dbh = C4::Context->dbh(); - my $isexception = 1; - my $insertException = $dbh->prepare("insert into special_holidays (branchcode,day,month,year,isexception,title,description) values (?,?,?,?,?,?,?)"); - $insertException->execute( $self->{branchcode}, $options{day},$options{month},$options{year}, $isexception, $options{title}, $options{description}); - $self->{'exception_holidays'}->{"$options{year}/$options{month}/$options{day}"}{title} = $options{title}; - $self->{'exception_holidays'}->{"$options{year}/$options{month}/$options{day}"}{description} = $options{description}; - - # changed the 'single_holidays' table, lets force/reset its cache - my $cache = Koha::Caches->get_instance(); - $cache->clear_from_cache( 'single_holidays') ; - $cache->clear_from_cache( 'exception_holidays') ; - - return $self; -} - -=head2 ModWeekdayholiday - - ModWeekdayholiday(weekday =>$weekday, - title => $title, - description => $description) - -Modifies the title and description of a weekday for $self->{branchcode}. - -C<$weekday> Is the title to update for the holiday. - -C<$description> Is the description to update for the holiday. - -=cut - -sub ModWeekdayholiday { - my $self = shift @_; - my %options = @_; - - my $dbh = C4::Context->dbh(); - my $updateHoliday = $dbh->prepare("UPDATE repeatable_holidays SET title = ?, description = ? WHERE branchcode = ? AND weekday = ?"); - $updateHoliday->execute( $options{title},$options{description},$self->{branchcode},$options{weekday}); - $self->{'week_days_holidays'}->{$options{weekday}}{title} = $options{title}; - $self->{'week_days_holidays'}->{$options{weekday}}{description} = $options{description}; - return $self; -} - -=head2 ModDaymonthholiday - - ModDaymonthholiday(day => $day, - month => $month, - title => $title, - description => $description); - -Modifies the title and description for a day/month holiday for $self->{branchcode}. - -C<$day> The day of the month for the update. - -C<$month> The month to be used for the update. - -C<$title> The title to be updated for the holiday. - -C<$description> The description to be update for the holiday. - -=cut - -sub ModDaymonthholiday { - my $self = shift @_; - my %options = @_; - - my $dbh = C4::Context->dbh(); - my $updateHoliday = $dbh->prepare("UPDATE repeatable_holidays SET title = ?, description = ? WHERE month = ? AND day = ? AND branchcode = ?"); - $updateHoliday->execute( $options{title},$options{description},$options{month},$options{day},$self->{branchcode}); - $self->{'day_month_holidays'}->{"$options{month}/$options{day}"}{title} = $options{title}; - $self->{'day_month_holidays'}->{"$options{month}/$options{day}"}{description} = $options{description}; - return $self; -} - -=head2 ModSingleholiday - - ModSingleholiday(day => $day, - month => $month, - year => $year, - title => $title, - description => $description); - -Modifies the title and description for a single holiday for $self->{branchcode}. - -C<$day> Is the day of the month to make the update. - -C<$month> Is the month to make the update. - -C<$year> Is the year to make the update. - -C<$title> Is the title to update for the holiday formed by $year/$month/$day. - -C<$description> Is the description to update for the holiday formed by $year/$month/$day. - -=cut - -sub ModSingleholiday { - my $self = shift @_; - my %options = @_; - - my $dbh = C4::Context->dbh(); - my $isexception = 0; - - my $updateHoliday = $dbh->prepare(" -UPDATE special_holidays SET title = ?, description = ? - WHERE day = ? AND month = ? AND year = ? AND branchcode = ? AND isexception = ?"); - $updateHoliday->execute($options{title},$options{description},$options{day},$options{month},$options{year},$self->{branchcode},$isexception); - $self->{'single_holidays'}->{"$options{year}/$options{month}/$options{day}"}{title} = $options{title}; - $self->{'single_holidays'}->{"$options{year}/$options{month}/$options{day}"}{description} = $options{description}; - - # changed the 'single_holidays' table, lets force/reset its cache - my $cache = Koha::Caches->get_instance(); - $cache->clear_from_cache( 'single_holidays') ; - $cache->clear_from_cache( 'exception_holidays') ; - - return $self; -} - -=head2 ModExceptionholiday - - ModExceptionholiday(day => $day, - month => $month, - year => $year, - title => $title, - description => $description); - -Modifies the title and description for an exception holiday for $self->{branchcode}. - -C<$day> Is the day of the month for the holiday. - -C<$month> Is the month for the holiday. - -C<$year> Is the year for the holiday. - -C<$title> Is the title to be modified for the holiday formed by $year/$month/$day. - -C<$description> Is the description to be modified for the holiday formed by $year/$month/$day. - -=cut - -sub ModExceptionholiday { - my $self = shift @_; - my %options = @_; - - my $dbh = C4::Context->dbh(); - my $isexception = 1; - my $updateHoliday = $dbh->prepare(" -UPDATE special_holidays SET title = ?, description = ? - WHERE day = ? AND month = ? AND year = ? AND branchcode = ? AND isexception = ?"); - $updateHoliday->execute($options{title},$options{description},$options{day},$options{month},$options{year},$self->{branchcode},$isexception); - $self->{'exception_holidays'}->{"$options{year}/$options{month}/$options{day}"}{title} = $options{title}; - $self->{'exception_holidays'}->{"$options{year}/$options{month}/$options{day}"}{description} = $options{description}; - - # changed the 'single_holidays' table, lets force/reset its cache - my $cache = Koha::Caches->get_instance(); - $cache->clear_from_cache( 'single_holidays') ; - $cache->clear_from_cache( 'exception_holidays') ; - - return $self; -} - -=head2 delete_holiday - - delete_holiday(weekday => $weekday - day => $day, - month => $month, - year => $year); - -Delete a holiday for $self->{branchcode}. - -C<$weekday> Is the week day to delete. - -C<$day> Is the day month to make the date to delete. - -C<$month> Is month to make the date to delete. - -C<$year> Is year to make the date to delete. - -=cut - -sub delete_holiday { - my $self = shift @_; - my %options = @_; - - # Verify what kind of holiday that day is. For example, if it is - # a repeatable holiday, this should check if there are some exception - # for that holiday rule. Otherwise, if it is a regular holiday, it´s - # ok just deleting it. - - my $dbh = C4::Context->dbh(); - my $isSingleHoliday = $dbh->prepare("SELECT id FROM special_holidays WHERE (branchcode = ?) AND (day = ?) AND (month = ?) AND (year = ?)"); - $isSingleHoliday->execute($self->{branchcode}, $options{day}, $options{month}, $options{year}); - if ($isSingleHoliday->rows) { - my $id = $isSingleHoliday->fetchrow; - $isSingleHoliday->finish; # Close the last query - - my $deleteHoliday = $dbh->prepare("DELETE FROM special_holidays WHERE id = ?"); - $deleteHoliday->execute($id); - delete($self->{'single_holidays'}->{"$options{year}/$options{month}/$options{day}"}); - } else { - $isSingleHoliday->finish; # Close the last query - - my $isWeekdayHoliday = $dbh->prepare("SELECT id FROM repeatable_holidays WHERE branchcode = ? AND weekday = ?"); - $isWeekdayHoliday->execute($self->{branchcode}, $options{weekday}); - if ($isWeekdayHoliday->rows) { - my $id = $isWeekdayHoliday->fetchrow; - $isWeekdayHoliday->finish; # Close the last query - - my $updateExceptions = $dbh->prepare("UPDATE special_holidays SET isexception = 0 WHERE (WEEKDAY(CONCAT(special_holidays.year,'-',special_holidays.month,'-',special_holidays.day)) = ?) AND (branchcode = ?)"); - $updateExceptions->execute($options{weekday}, $self->{branchcode}); - $updateExceptions->finish; # Close the last query - - my $deleteHoliday = $dbh->prepare("DELETE FROM repeatable_holidays WHERE id = ?"); - $deleteHoliday->execute($id); - delete($self->{'week_days_holidays'}->{$options{weekday}}); - } else { - $isWeekdayHoliday->finish; # Close the last query - - my $isDayMonthHoliday = $dbh->prepare("SELECT id FROM repeatable_holidays WHERE (branchcode = ?) AND (day = ?) AND (month = ?)"); - $isDayMonthHoliday->execute($self->{branchcode}, $options{day}, $options{month}); - if ($isDayMonthHoliday->rows) { - my $id = $isDayMonthHoliday->fetchrow; - $isDayMonthHoliday->finish; - my $updateExceptions = $dbh->prepare("UPDATE special_holidays SET isexception = 0 WHERE (special_holidays.branchcode = ?) AND (special_holidays.day = ?) and (special_holidays.month = ?)"); - $updateExceptions->execute($self->{branchcode}, $options{day}, $options{month}); - $updateExceptions->finish; # Close the last query - - my $deleteHoliday = $dbh->prepare("DELETE FROM repeatable_holidays WHERE (id = ?)"); - $deleteHoliday->execute($id); - delete($self->{'day_month_holidays'}->{"$options{month}/$options{day}"}); - } - } - } - - # changed the 'single_holidays' table, lets force/reset its cache - my $cache = Koha::Caches->get_instance(); - $cache->clear_from_cache( 'single_holidays') ; - $cache->clear_from_cache( 'exception_holidays') ; - - return $self; -} -=head2 delete_holiday_range - - delete_holiday_range(day => $day, - month => $month, - year => $year); - -Delete a holiday range of dates for $self->{branchcode}. - -C<$day> Is the day month to make the date to delete. - -C<$month> Is month to make the date to delete. - -C<$year> Is year to make the date to delete. - -=cut - -sub delete_holiday_range { - my $self = shift; - my %options = @_; - - my $dbh = C4::Context->dbh(); - my $sth = $dbh->prepare("DELETE FROM special_holidays WHERE (branchcode = ?) AND (day = ?) AND (month = ?) AND (year = ?)"); - $sth->execute($self->{branchcode}, $options{day}, $options{month}, $options{year}); - - # changed the 'single_holidays' table, lets force/reset its cache - my $cache = Koha::Caches->get_instance(); - $cache->clear_from_cache( 'single_holidays') ; - $cache->clear_from_cache( 'exception_holidays') ; - -} - -=head2 delete_holiday_range_repeatable - - delete_holiday_range_repeatable(day => $day, - month => $month); - -Delete a holiday for $self->{branchcode}. - -C<$day> Is the day month to make the date to delete. - -C<$month> Is month to make the date to delete. - -=cut - -sub delete_holiday_range_repeatable { - my $self = shift; - my %options = @_; - - my $dbh = C4::Context->dbh(); - my $sth = $dbh->prepare("DELETE FROM repeatable_holidays WHERE (branchcode = ?) AND (day = ?) AND (month = ?)"); - $sth->execute($self->{branchcode}, $options{day}, $options{month}); -} - -=head2 delete_exception_holiday_range - - delete_exception_holiday_range(weekday => $weekday - day => $day, - month => $month, - year => $year); - -Delete a holiday for $self->{branchcode}. - -C<$day> Is the day month to make the date to delete. - -C<$month> Is month to make the date to delete. - -C<$year> Is year to make the date to delete. - -=cut - -sub delete_exception_holiday_range { - my $self = shift; - my %options = @_; - - my $dbh = C4::Context->dbh(); - my $sth = $dbh->prepare("DELETE FROM special_holidays WHERE (branchcode = ?) AND (isexception = 1) AND (day = ?) AND (month = ?) AND (year = ?)"); - $sth->execute($self->{branchcode}, $options{day}, $options{month}, $options{year}); - - # changed the 'single_holidays' table, lets force/reset its cache - my $cache = Koha::Caches->get_instance(); - $cache->clear_from_cache( 'single_holidays') ; - $cache->clear_from_cache( 'exception_holidays') ; -} - -=head2 isHoliday - - $isHoliday = isHoliday($day, $month $year); - -C<$day> Is the day to check whether if is a holiday or not. - -C<$month> Is the month to check whether if is a holiday or not. - -C<$year> Is the year to check whether if is a holiday or not. - -=cut - -sub isHoliday { - my ($self, $day, $month, $year) = @_; - # FIXME - date strings are stored in non-padded metric format. should change to iso. - $month=$month+0; - $year=$year+0; - $day=$day+0; - my $weekday = &Date::Calc::Day_of_Week($year, $month, $day) % 7; - my $weekDays = $self->get_week_days_holidays(); - my $dayMonths = $self->get_day_month_holidays(); - my $exceptions = $self->get_exception_holidays(); - my $singles = $self->get_single_holidays(); - if (defined($exceptions->{"$year/$month/$day"})) { - return 0; - } else { - if ((exists($weekDays->{$weekday})) || - (exists($dayMonths->{"$month/$day"})) || - (exists($singles->{"$year/$month/$day"}))) { - return 1; - } else { - return 0; - } - } - -} - -=head2 copy_to_branch - - $calendar->copy_to_branch($target_branch) - -=cut - -sub copy_to_branch { - my ($self, $target_branch) = @_; - - croak "No target_branch" unless $target_branch; - - my $target_calendar = C4::Calendar->new(branchcode => $target_branch); - - my ($y, $m, $d) = Today(); - my $today = sprintf ISO_DATE_FORMAT, $y,$m,$d; - - my $wdh = $self->get_week_days_holidays; - $target_calendar->insert_week_day_holiday( weekday => $_, %{ $wdh->{$_} } ) - foreach keys %$wdh; - $target_calendar->insert_day_month_holiday(%$_) - foreach values %{ $self->get_day_month_holidays }; - $target_calendar->insert_exception_holiday(%$_) - foreach grep { $_->{date} gt $today } values %{ $self->get_exception_holidays }; - $target_calendar->insert_single_holiday(%$_) - foreach grep { $_->{date} gt $today } values %{ $self->get_single_holidays }; - - return 1; -} - -1; - -__END__ - -=head1 AUTHOR - -Koha Physics Library UNLP - -=cut --- a/C4/Circulation.pm +++ a/C4/Circulation.pm @@ -1198,7 +1198,7 @@ sub checkHighHolds { my $issuedate = DateTime->now( time_zone => C4::Context->tz() ); - my $calendar = Koha::DiscreteCalendar->new( branchcode => $branch ); + my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branch }); my $itype = $item_object->effective_itemtype; my $orig_due = C4::Circulation::CalcDateDue( $issuedate, $itype, $branch, $borrower ); @@ -1725,7 +1725,7 @@ Returns a book. removed. Optional. =item C<$dropbox> indicates that the check-in date is assumed to be -yesterday, or the last non-holiday as defined in C4::Calendar . If +yesterday, or the last non-holiday as defined in Koha::DiscreteCalendar. If overdue charges are applied and C<$dropbox> is true, the last charge will be removed. This assumes that the fines accrual script has run for _today_. Optional. @@ -2128,7 +2128,7 @@ sub MarkIssueReturned { my $query = 'UPDATE issues SET returndate='; my @bind; if ($dropbox_branch) { - my $calendar = Koha::DiscreteCalendar->new( branchcode => $dropbox_branch ); + my $calendar = Koha::DiscreteCalendar->new({ branchcode => $dropbox_branch }); my $dropboxdate = $calendar->addDate( DateTime->now( time_zone => C4::Context->tz), -1 ); $query .= ' ? '; push @bind, $dropboxdate->strftime('%Y-%m-%d %H:%M'); @@ -3482,7 +3482,7 @@ sub CalcDateDue { else { # days $dur = DateTime::Duration->new( days => $loanlength->{$length_key}); } - my $calendar = Koha::DiscreteCalendar->new( branchcode => $branch ); + my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branch }); $datedue = $calendar->addDate( $datedue, $dur, $loanlength->{lengthunit} ); if ($loanlength->{lengthunit} eq 'days') { $datedue->set_hour(23); @@ -3521,14 +3521,13 @@ sub CalcDateDue { } } if ( C4::Context->preference('useDaysMode') ne 'Days' ) { - my $calendar = Koha::DiscreteCalendar->new( branchcode => $branch ); + my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branch }); if ( $calendar->is_holiday($datedue) ) { # Don't return on a closed day - $datedue = $calendar->prev_open_day( $datedue ); + $datedue = $calendar->prev_open_day( $datedue )->set(hour => 23, minute => 59); } } } - return $datedue; } --- a/C4/HoldsQueue.pm +++ a/C4/HoldsQueue.pm @@ -77,7 +77,7 @@ sub TransportCostMatrix { }; if ( C4::Context->preference("HoldsQueueSkipClosed") ) { - $calendars->{$from} ||= Koha::DiscreteCalendar->new( branchcode => $from ); + $calendars->{$from} ||= Koha::DiscreteCalendar->new({ branchcode => $from }); $transport_cost_matrix{$to}{$from}{disable_transfer} ||= $calendars->{$from}->is_holiday( $today ); } @@ -742,7 +742,7 @@ sub load_branches_to_pull_from { my $today = dt_from_string(); if ( C4::Context->preference('HoldsQueueSkipClosed') ) { @branches_to_use = grep { - !Koha::DiscreteCalendar->new( branchcode => $_ ) + !Koha::DiscreteCalendar->new({ branchcode => $_ }) ->is_holiday( $today ) } @branches_to_use; } --- a/C4/Overdues.pm +++ a/C4/Overdues.pm @@ -294,7 +294,7 @@ sub get_chargeable_units { my $charge_duration; if ($unit eq 'hours') { if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') { - my $calendar = Koha::DiscreteCalendar->new( branchcode => $branchcode ); + my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branchcode }); $charge_duration = $calendar->hours_between( $date_due, $date_returned ); } else { $charge_duration = $date_returned->delta_ms( $date_due ); @@ -306,7 +306,7 @@ sub get_chargeable_units { } else { # days if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') { - my $calendar = Koha::DiscreteCalendar->new( branchcode => $branchcode ); + my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branchcode }); $charge_duration = $calendar->days_between( $date_due, $date_returned ); } else { $charge_duration = $date_returned->delta_days( $date_due ); --- a/C4/Reserves.pm +++ a/C4/Reserves.pm @@ -781,7 +781,7 @@ sub CancelExpiredReserves { my $holds = Koha::Holds->search( $params ); while ( my $hold = $holds->next ) { - my $calendar = Koha::DiscreteCalendar->new( branchcode => $hold->branchcode ); + my $calendar = Koha::DiscreteCalendar->new({ branchcode => $hold->branchcode }); next if !$cancel_on_holidays && $calendar->is_holiday( $today ); --- a/Koha/DiscreteCalendar.pm +++ a/Koha/DiscreteCalendar.pm @@ -16,13 +16,13 @@ package Koha::DiscreteCalendar; # along with Koha; if not, see . #####Sets holiday periods for each branch. Datedues will be extended if branch is closed -TG -use strict; -use warnings; +use Modern::Perl; use CGI qw ( -utf8 ); use Carp; use DateTime; use DateTime::Format::Strptime; +use Data::Dumper; use C4::Context; use C4::Output; @@ -48,7 +48,7 @@ Koha::DiscreteCalendar - Object containing a branches calendar, working with the use Koha::DiscreteCalendar - my $c = Koha::DiscreteCalendar->new( branchcode => 'MAIN' ); + my $c = Koha::DiscreteCalendar->new({ branchcode => 'MAIN' }); my $dt = DateTime->now(); # are we open @@ -66,19 +66,19 @@ Koha::DiscreteCalendar - Object containing a branches calendar, working with the =head2 new : Create a (discrete) calendar object -my $calendar = Koha::DiscreteCalendar->new( branchcode => 'MAIN' ); +my $calendar = Koha::DiscreteCalendar->new({ branchcode => 'MAIN' }); The option branchcode is required =cut sub new { - my ( $classname, %options ) = @_; + my ( $classname, $options ) = @_; my $self = {}; bless $self, $classname; - for my $o_name ( keys %options ) { + for my $o_name ( keys %{ $options } ) { my $o = lc $o_name; - $self->{$o} = $options{$o_name}; + $self->{$o} = $options->{$o_name}; } if ( !defined $self->{branchcode} ) { croak 'No branchcode argument passed to Koha::DiscreteCalendar->new'; @@ -157,7 +157,7 @@ sub get_dates_info { Koha::DiscreteCalendar->add_new_branch($copyBranch, $newBranch) -This methode will copy everything from a given branch to a new branch +This method will copy everything from a given branch to a new branch C<$copyBranch> is the branch to copy from C<$newBranch> is the branch to be created, and copy into @@ -240,7 +240,7 @@ Returns the furthest date available in the databse of current branch. sub get_max_date { my $self = shift; - my $branchcode = $self->{branchcode}; + my $branchcode = $self->{branchcode}; my $schema = Koha::Database->new->schema; my $rs = $schema->resultset('DiscreteCalendar')->search( @@ -505,23 +505,26 @@ sub edit_holiday { my $close_hour = $params->{close_hour} || ''; my $delete_type = $params->{delete_type} || undef; - my $today = $params->{today} || DateTime->today; + my $today = $params->{today} || dt_from_string()->truncate( to => 'day' ); my $branchcode = $self->{branchcode}; + # When override param is set, this function will allow past dates to be set as holidays, + # otherwise it will not. This is meant to only be used for testing. + my $override = $params->{override} || 0; + my $schema = Koha::Database->new->schema; $schema->{AutoCommit} = 0; $schema->storage->txn_begin; - my $dtf = $schema->storage->datetime_parser; + my $dtf = DateTime::Format::Strptime->new(pattern => "%F %T"); #String dates for Database usage - my $start_date_string = $dtf->format_datetime($start_date); - my $end_date_string = $dtf->format_datetime($end_date); - $today = $dtf->format_datetime($today); - + my $start_date_string = $dtf->format_datetime($start_date->clone->truncate(to => 'day')); + my $end_date_string = $dtf->format_datetime($end_date->clone->truncate(to => 'day')); + $today = $dtf->format_datetime($today->clone->truncate(to => 'day')); my %updateValues = ( is_opened => 0, - note => $title, + note => $title, holiday_type => $holiday_type, ); $updateValues{open_hour} = $open_hour if $open_hour ne ''; @@ -537,7 +540,7 @@ sub edit_holiday { branchcode => $branchcode, }, { - where => \[ 'DAYOFWEEK(date) = ? AND date >= ? AND date <= DATE(?)', $weekday, $start_date_string, $end_date_string], + where => \[ 'DAYOFWEEK(date) = ? AND date >= DATE(?) AND date <= DATE(?)', $weekday, $start_date_string, $end_date_string], } ); @@ -546,10 +549,12 @@ sub edit_holiday { } }elsif ($holiday_type eq $HOLIDAYS->{EXCEPTION} || $holiday_type eq $HOLIDAYS->{FLOAT} || $holiday_type eq $HOLIDAYS->{NEED_VALIDATION}) { #Update Exception Float and Needs Validation holidays - my $where = { date => { -between => [$start_date_string, $end_date_string], '>=' => $today}}; + my $where = { date => { -between => [$start_date_string, $end_date_string]}}; if($start_date_string ne $end_date_string && $weekday && $weekday ne 'everyday'){ - $where = {-and => [ \["DAYOFWEEK(date) = ?", $weekday], date => { -between => [$start_date_string, $end_date_string], '>=' => $today}]}; + $where = {-and => [ \["DAYOFWEEK(date) = ?", $weekday], date => { -between => [$start_date_string, $end_date_string]}]}; } + $where->{date}{'>='} = $today unless $override; + my $rs = $schema->resultset('DiscreteCalendar')->search( { branchcode => $branchcode, @@ -585,10 +590,12 @@ sub edit_holiday { }else { #Update date(s)/Remove holidays - my $where = { date => { -between => [$start_date_string, $end_date_string], '>=' => $today}}; + my $where = { date => { -between => [$start_date_string, $end_date_string]}}; if($start_date_string ne $end_date_string && $weekday && $weekday ne 'everyday'){ - $where = {-and => [ \["DAYOFWEEK(date) = ?", $weekday], date => { -between => [$start_date_string, $end_date_string], '>=' => $today}]}; + $where = {-and => [ \["DAYOFWEEK(date) = ?", $weekday], date => { -between => [$start_date_string, $end_date_string]}]}; } + $where->{date}{'>='} = $today unless $override; + my $rs = $schema->resultset('DiscreteCalendar')->search( { branchcode => $branchcode, @@ -794,9 +801,8 @@ sub is_holiday { } ); - if($rs->count() != 0){ - $isHoliday = 0 if $rs->first()->is_opened(); - $isHoliday = 1 if !$rs->first()->is_opened(); + if ($rs->count() != 0) { + $isHoliday = ($rs->first()->is_opened() ? 0 : 1); } return $isHoliday; @@ -955,15 +961,13 @@ sub days_between { if ( $start_date->compare($end_date) > 0 ) { # swap dates - my $int_dt = $end_date; - $end_date = $start_date; - $start_date = $int_dt; + ($start_date, $end_date) = ($end_date, $start_date); } my $schema = Koha::Database->new->schema; my $dtf = $schema->storage->datetime_parser; - $start_date = $dtf->format_datetime($start_date); - $end_date = $dtf->format_datetime($end_date); + $start_date = $dtf->format_datetime($start_date->clone->truncate(to => 'day')); + $end_date = $dtf->format_datetime($end_date->clone->truncate(to => 'day')); my $days_between = $schema->resultset('DiscreteCalendar')->search( { @@ -1118,7 +1122,7 @@ sub open_hours_between { my ($self, $start_date, $end_date) = @_; my $branchcode = $self->{branchcode}; my $schema = Koha::Database->new->schema; - my $dtf = $schema->storage->datetime_parser; + my $dtf = DateTime::Format::Strptime->new(pattern => "%F %T"); $start_date = $dtf->format_datetime($start_date); $end_date = $dtf->format_datetime($end_date); --- a/Koha/Hold.pm +++ a/Koha/Hold.pm @@ -62,7 +62,7 @@ sub age { my $age; if ( $use_calendar ) { - my $calendar = Koha::DiscreteCalendar->new( branchcode => $self->branchcode ); + my $calendar = Koha::DiscreteCalendar->new({ branchcode => $self->branchcode }); $age = $calendar->days_between( dt_from_string( $self->reservedate ), $today ); } else { @@ -164,7 +164,7 @@ sub set_waiting { my $max_pickup_delay = C4::Context->preference("ReservesMaxPickUpDelay"); my $cancel_on_holidays = C4::Context->preference('ExpireReservesOnHolidays'); - my $calendar = Koha::DiscreteCalendar->new( branchcode => $self->branchcode ); + my $calendar = Koha::DiscreteCalendar->new({ branchcode => $self->branchcode }); my $expirationdate = $today->clone; $expirationdate->add(days => $max_pickup_delay); --- a/circ/returns.pl +++ a/circ/returns.pl @@ -204,7 +204,7 @@ my $dropboxmode = $query->param('dropboxmode'); my $dotransfer = $query->param('dotransfer'); my $canceltransfer = $query->param('canceltransfer'); my $dest = $query->param('dest'); -my $calendar = Koha::DiscreteCalendar->new( branchcode => $userenv_branch ); +my $calendar = Koha::DiscreteCalendar->new({ branchcode => $userenv_branch }); #dropbox: get last open day (today - 1) my $today = DateTime->now( time_zone => C4::Context->tz()); my $dropboxdate = $calendar->addDate($today, -1); --- a/installer/data/mysql/atomicupdate/bug_17015_part1_create_discrete_calendar.sql +++ a/installer/data/mysql/atomicupdate/bug_17015_part1_create_discrete_calendar.sql @@ -11,4 +11,4 @@ CREATE TABLE `discrete_calendar` ( `open_hour` time NOT NULL, `close_hour` time NOT NULL, PRIMARY KEY (`branchcode`,`date`) -); +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; --- a/installer/data/mysql/atomicupdate/bug_17015_part2_fill_discrete_calendar.perl +++ a/installer/data/mysql/atomicupdate/bug_17015_part2_fill_discrete_calendar.perl @@ -3,8 +3,7 @@ # # Script that fills the discrete_calendar table with dates, using the other date-related tables # -use strict; -use warnings; +use Modern::Perl; use DateTime; use DateTime::Format::Strptime; use Data::Dumper; @@ -12,25 +11,8 @@ use Getopt::Long; use C4::Context; # Options -my $help = 0; my $daysInFuture = 365; -GetOptions ( - 'days|?|d=i' => \$daysInFuture, - 'help|?|h' => \$help); -my $usage = << 'ENDUSAGE'; -Script that manages the discrete_calendar table. - -This script has the following parameters : - --days --d : how many days in the future will be created, by default it's 365 - -h --help: this message - -ENDUSAGE - -if ($help) { - print $usage; - exit; -} my $dbh = C4::Context->dbh; $dbh->{AutoCommit} = 0; $dbh->{RaiseError} = 1; --- a/installer/data/mysql/atomicupdate/bug_17015_part3_drop_calendar.sql +++ a/installer/data/mysql/atomicupdate/bug_17015_part3_drop_calendar.sql @@ -0,0 +1,5 @@ +-- Bugzilla 17015 +-- New koha calendar +-- Drop deprecated calendar-related tables after creating and filling discrete_calendar +DROP TABLE IF EXISTS `repeatable_holidays`; +DROP TABLE IF EXISTS `special_holidays`; --- a/installer/data/mysql/kohastructure.sql +++ a/installer/data/mysql/kohastructure.sql @@ -680,6 +680,22 @@ CREATE TABLE `deleteditems` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- +-- Table structure for table `discrete_calendar` +-- + +DROP TABLE IF EXISTS `discrete_calendar`; +CREATE TABLE `discrete_calendar` ( + `date` datetime NOT NULL, + `branchcode` varchar(10) NOT NULL, + `is_opened` tinyint(1) DEFAULT 1, + `holiday_type` varchar(1) DEFAULT '', + `note` varchar(30) DEFAULT '', + `open_hour` time NOT NULL, + `close_hour` time NOT NULL, + PRIMARY KEY (`branchcode`,`date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- -- Table structure for table `export_format` -- @@ -1379,22 +1395,6 @@ CREATE TABLE `printers_profile` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- --- Table structure for table `repeatable_holidays` --- - -DROP TABLE IF EXISTS `repeatable_holidays`; -CREATE TABLE `repeatable_holidays` ( -- information for the days the library is closed - `id` int(11) NOT NULL auto_increment, -- unique identifier assigned by Koha - `branchcode` varchar(10) NOT NULL default '', -- foreign key from the branches table, defines which branch this closing is for - `weekday` smallint(6) default NULL, -- day of the week (0=Sunday, 1=Monday, etc) this closing is repeated on - `day` smallint(6) default NULL, -- day of the month this closing is on - `month` smallint(6) default NULL, -- month this closing is in - `title` varchar(50) NOT NULL default '', -- title of this closing - `description` MEDIUMTEXT NOT NULL, -- description for this closing - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- -- Table structure for table `reports_dictionary` -- @@ -1970,23 +1970,6 @@ CREATE TABLE `reviews` ( -- patron opac comments ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- --- Table structure for table `special_holidays` --- - -DROP TABLE IF EXISTS `special_holidays`; -CREATE TABLE `special_holidays` ( -- non repeatable holidays/library closings - `id` int(11) NOT NULL auto_increment, -- unique identifier assigned by Koha - `branchcode` varchar(10) NOT NULL default '', -- foreign key from the branches table, defines which branch this closing is for - `day` smallint(6) NOT NULL default 0, -- day of the month this closing is on - `month` smallint(6) NOT NULL default 0, -- month this closing is in - `year` smallint(6) NOT NULL default 0, -- year this closing is in - `isexception` smallint(1) NOT NULL default 1, -- is this a holiday exception to a repeatable holiday (1 for yes, 0 for no) - `title` varchar(50) NOT NULL default '', -- title for this closing - `description` MEDIUMTEXT NOT NULL, -- description of this closing - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- -- Table structure for table `statistics` -- --- a/installer/data/mysql/updatedatabase.pl +++ a/installer/data/mysql/updatedatabase.pl @@ -14372,7 +14372,7 @@ if( CheckVersion( $DBversion ) ) { my $expirationdate = dt_from_string($hold->waitingdate); if ( C4::Context->preference("ExcludeHolidaysFromMaxPickUpDelay") ) { - my $calendar = Koha::DiscreteCalendar->new( branchcode => $hold->branchcode ); + my $calendar = Koha::DiscreteCalendar->new({ branchcode => $hold->branchcode }); $expirationdate = $calendar->days_forward( $expirationdate, $max_pickup_delay ); } else { $expirationdate->add( days => $max_pickup_delay ); --- a/koha-tmpl/intranet-tmpl/prog/css/discretecalendar.css +++ a/koha-tmpl/intranet-tmpl/prog/css/discretecalendar.css @@ -0,0 +1,206 @@ +#jcalendar-container .ui-datepicker { + font-size:185%; +} + +#holidayweeklyrepeatable, +#holidaysyearlyrepeatable, +#holidaysunique, +#holidayexceptions { + font-size:90%; + margin-bottom:1em; +} + +#showHoliday { + margin:.5em 0; +} + +.key { + padding:3px; + white-space:nowrap; + line-height:230%; +} + +.ui-datepicker { + font-size:150%; +} + +.ui-datepicker th, +.ui-datepicker .ui-datepicker-title select { + font-size:80%; +} + +.ui-datepicker td a { + padding:.5em; +} + +.ui-datepicker td span { + padding:.5em; + border:1px solid #BCBCBC; +} + +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { + font-size:80%; +} + +.key { + padding:3px; + white-space:nowrap; + line-height:230%; +} + +.normalday { + background-color:#EDEDED; + color:#000; + border:1px solid #BCBCBC; +} + +.ui-datepicker-unselectable { + padding:.5em; + white-space:nowrap; +} + +.ui-state-disabled { + padding:.5em; + white-space:nowrap; +} + +.exception { + background-color:#b3d4ff; + color:#000; + border:1px solid #BCBCBC; +} + +.past-date { + background-color:#e6e6e6; + color:#555; + border:1px solid #BCBCBC; +} + +td.past-date a.ui-state-default { + background:#e6e6e6; + color:#555; +} + +.float { + background-color:#6f3; + color:#000; + border:1px solid #BCBCBC; +} + +.holiday { + background-color:#ffaeae; + color:#000; + border:1px solid #BCBCBC; +} + +.repeatableweekly { + background-color:#FF9; + color:#000; + border:1px solid #BCBCBC; +} + +.repeatableyearly { + background-color:#FC6; + color:#000; + border:1px solid #BCBCBC; +} + +td.exception a.ui-state-default { + background:#b3d4ff none; + color:#000; + border:1px solid #BCBCBC; +} + +td.float a.ui-state-default { + background:#6f3 none; + color:#000; + border:1px solid #BCBCBC; +} + +td.holiday a.ui-state-default { + background:#ffaeae none; + color:#000; + border:1px solid #BCBCBC; +} + +td.repeatableweekly a.ui-state-default { + background:#FF9 none; + color:#000; + border:1px solid #BCBCBC; +} + +td.repeatableyearly a.ui-state-default { + background:#FC6 none; + color:#000; + border:1px solid #BCBCBC; +} + +.information { + background-color:#DCD2F1; + width:300px; + display:none; + border:1px solid #000; + color:#000; + font-size:8pt; + font-weight:700; + background-color:#FFD700; + cursor:pointer; + padding:2px; +} + +.panel { + z-index:1; + display:none; + border:3px solid #CCC; + padding:3px; + margin-top:.3em; + background-color:#FEFEFE; +} + +fieldset.brief { + border:0; + margin-top:0; +} + +h1 select { + width:20em; +} + +div.yui-b fieldset.brief ol { + font-size:100%; +} + +div.yui-b fieldset.brief li, +div.yui-b fieldset.brief li.radio { + padding:.2em 0; +} + +.help { + margin:.3em 0; + border:1px solid #EEE; + padding:.3em .7em; + font-size:90%; +} + +.calendar td, +.calendar th, +.calendar .button, +.calendar tbody .day { + padding:.7em; + font-size:110%; +} + +.calendar { + width:auto; + border:0; +} + +.copyHoliday form li { + display:table-row; +} + +.copyHoliday form li b, +.copyHoliday form li input { + display:table-cell; + margin-bottom:2px; +} --- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/discrete_calendar.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/discrete_calendar.tt @@ -1,115 +1,411 @@ [% USE Branches %] +[% SET footerjs = 1 %] [% INCLUDE 'doc-head-open.inc' %] Koha › Tools › [% Branches.GetName( branch ) %] calendar [% INCLUDE 'doc-head-close.inc' %] [% INCLUDE 'calendar.inc' %] - - + + [% INCLUDE 'datatables.inc' %] - + - - - - - -[% INCLUDE 'header.inc' %] -[% INCLUDE 'cat-search.inc' %] - - - -
- -
-
-
-

[% Branches.GetName( branch ) %] calendar

-
-
- -
- - Copy calendar to - - - -
-

Calendar information

-
- - [% UNLESS datesInfos %] -
- Error! You have to run generate_discrete_calendar.pl in order to use Discrete Calendar. -
- [% END %] - - [% IF no_branch_selected %] -
- No library set! You are using the default calendar. -
- [% END %] - -
-
-
-

Edit date details

- -
    -
  1. - Library: - - -
  2. -
  3. - From date: - , - - [% IF ( dateformat == "us" ) %]//[% ELSIF ( dateformat == "metric" ) %]//[% ELSIF ( dateformat == "dmydot" ) %]..[% ELSE %]//[% END %] - - - - - -
  4. -
  5. - To date: - -
  6. -
  7. - -
  8. -
  9. - - -
  10. - - -
  11. - -
  12. -
  13. - -
  14. -
  15. - - -
  16. -
  17. - - - - - - - - - - -
  18. -
-
- - Cancel -
-
-
-
- - - - - -
-
-
-

Hints

-
    -
  • Search in the calendar the day you want to set as holiday.
  • -
  • Click the date to add or edit a holiday.
  • -
  • Specify how the holiday should repeat.
  • -
  • Click Save to finish.
  • -
  • PS: -
      -
    • You can't edit passed dates
    • -
    • Weekly holidays change open/close hours for all the days affected unless inputs are empty
    • -
    -
  • -
-

Key

-

- Working day - Unique holiday - Holiday repeating weekly - Holiday repeating yearly - Floating holiday - Need validation -

-
-
- - [% IF ( NEED_VALIDATION_HOLIDAYS ) %] -

Need validation holidays

- - - - - - - - - [% FOREACH need_validation_holiday IN NEED_VALIDATION_HOLIDAYS %] - - - - - [% END %] - -
DateTitle
[% need_validation_holiday.outputdate %][% need_validation_holiday.note %]
- [% END %] - - [% IF ( WEEKLY_HOLIDAYS ) %] -

Weekly - Repeatable holidays

- - - - - - - - - [% FOREACH WEEK_DAYS_LOO IN WEEKLY_HOLIDAYS %] - - - - - - [% END %] - -
Day of weekTitle
[% WEEK_DAYS_LOO.weekday %][% WEEK_DAYS_LOO.note %]
-[% END %] - -[% IF ( REPEATABLE_HOLIDAYS ) %] -

Yearly - Repeatable holidays

- - - - [% IF ( dateformat == "metric" ) %] - - [% ELSE %] - - [% END %] - - - - - [% FOREACH DAY_MONTH_HOLIDAYS_LOO IN REPEATABLE_HOLIDAYS %] - - [% IF ( dateformat == "metric" ) %] - - [% ELSE %] - - [% END %] - - - [% END %] - -
Day/monthMonth/dayTitle
[% DAY_MONTH_HOLIDAYS_LOO.day %]/[% DAY_MONTH_HOLIDAYS_LOO.month %][% DAY_MONTH_HOLIDAYS_LOO.month %]/[% DAY_MONTH_HOLIDAYS_LOO.day %][% DAY_MONTH_HOLIDAYS_LOO.note %]
+ [% END %] - -[% IF ( UNIQUE_HOLIDAYS ) %] -

Unique holidays

- - - - - - - - - [% FOREACH HOLIDAYS_LOO IN UNIQUE_HOLIDAYS %] - - - - - [% END %] - -
DateTitle
[% HOLIDAYS_LOO.outputdate %][% HOLIDAYS_LOO.note %]
-[% END %] - -[% IF ( FLOAT_HOLIDAYS ) %] -

Floating holidays

- - - - - - - - - [% FOREACH float_holiday IN FLOAT_HOLIDAYS %] - - - - - [% END %] - -
DateTitle
[% float_holiday.outputdate %][% float_holiday.note %]
-[% END %] -
-
-
-
-
- -
-[% INCLUDE 'tools-menu.inc' %] -
-
[% INCLUDE 'intranet-bottom.inc' %] --- a/misc/cronjobs/add_days_discrete_calendar.pl +++ a/misc/cronjobs/add_days_discrete_calendar.pl @@ -3,8 +3,7 @@ # # This script adds one day into discrete_calendar table based on the same day from the week before # -use strict; -use warnings; +use Modern::Perl; use DateTime; use DateTime::Format::Strptime; use Data::Dumper; --- a/misc/cronjobs/fines.pl +++ a/misc/cronjobs/fines.pl @@ -176,7 +176,7 @@ EOM sub set_holiday { my ( $branch, $dt ) = @_; - my $calendar = Koha::DiscreteCalendar->new( branchcode => $branch ); + my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branch }); return $calendar->is_holiday($dt); } --- a/misc/cronjobs/overdue_notices.pl +++ a/misc/cronjobs/overdue_notices.pl @@ -445,7 +445,7 @@ elsif ( defined $text_filename ) { foreach my $branchcode (@branches) { if ( C4::Context->preference('OverdueNoticeCalendar') ) { - my $calendar = Koha::DiscreteCalendar->new( branchcode => $branchcode ); + my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branchcode }); if ( $calendar->is_holiday($date_to_run) ) { next; } @@ -546,7 +546,7 @@ END_SQL my $days_between; if ( C4::Context->preference('OverdueNoticeCalendar') ) { - my $calendar = Koha::DiscreteCalendar->new( branchcode => $branchcode ); + my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branchcode }); $days_between = $calendar->days_between( dt_from_string($data->{date_due}), $date_to_run ); } else { @@ -626,7 +626,7 @@ END_SQL my $exceededPrintNoticesMaxLines = 0; while ( my $item_info = $sth2->fetchrow_hashref() ) { if ( C4::Context->preference('OverdueNoticeCalendar') ) { - my $calendar = Koha::DiscreteCalendar->new( branchcode => $branchcode ); + my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branchcode }); $days_between = $calendar->days_between( dt_from_string( $item_info->{date_due} ), $date_to_run ); } else { --- a/misc/cronjobs/staticfines.pl +++ a/misc/cronjobs/staticfines.pl @@ -176,7 +176,7 @@ for ( my $i = 0 ; $i < scalar(@$data) ; $i++ ) { my $calendar; unless ( defined( $calendars{$branchcode} ) ) { - $calendars{$branchcode} = Koha::DiscreteCalendar->new( branchcode => $branchcode ); + $calendars{$branchcode} = Koha::DiscreteCalendar->new({ branchcode => $branchcode }); } $calendar = $calendars{$branchcode}; my $isHoliday = $calendar->isHoliday( $tday, $tmonth, $tyear ); --- a/misc/cronjobs/thirdparty/TalkingTech_itiva_outbound.pl +++ a/misc/cronjobs/thirdparty/TalkingTech_itiva_outbound.pl @@ -294,7 +294,7 @@ sub GetWaitingHolds { $sth->execute(); my @results; while ( my $issue = $sth->fetchrow_hashref() ) { - my $calendar = Koha::DiscreteCalendar->new( branchcode => $issue->{'site'} ); + my $calendar = Koha::DiscreteCalendar->new({ branchcode => $issue->{'site'} }); my $waiting_date = dt_from_string( $issue->{waitingdate}, 'sql' ); my $pickup_date = $waiting_date->clone->add( days => $pickupdelay ); --- a/t/db_dependent/Circulation/CalcDateDue.t +++ a/t/db_dependent/Circulation/CalcDateDue.t @@ -8,7 +8,8 @@ use DBI; use DateTime; use t::lib::Mocks; use t::lib::TestBuilder; -use C4::Calendar; +use Koha::DateUtils; +use Koha::DiscreteCalendar; use_ok('C4::Circulation'); @@ -39,12 +40,11 @@ t::lib::Mocks::mock_preference('useDaysMode', 'Days'); my $cache = Koha::Caches->get_instance(); $cache->clear_from_cache('single_holidays'); -my $dateexpiry = '2013-01-01'; - +my $dateexpiry = dt_from_string->truncate(to => 'day')->add(days => 30, hours => 23, minutes => 59)->iso8601; my $borrower = {categorycode => 'B', dateexpiry => $dateexpiry}; -my $start_date = DateTime->new({year => 2013, month => 2, day => 9}); +my $start_date =dt_from_string->truncate(to => 'day')->add(days => 60); my $date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower ); -is($date, $dateexpiry . 'T23:59:00', 'date expiry'); +is($date, $dateexpiry, 'date expiry'); $date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower, 1 ); @@ -53,31 +53,30 @@ t::lib::Mocks::mock_preference('ReturnBeforeExpiry', 1); t::lib::Mocks::mock_preference('useDaysMode', 'noDays'); $borrower = {categorycode => 'B', dateexpiry => $dateexpiry}; -$start_date = DateTime->new({year => 2013, month => 2, day => 9}); +$start_date =dt_from_string->truncate(to => 'day')->add(days => 60); $date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower ); -is($date, $dateexpiry . 'T23:59:00', 'date expiry with useDaysMode to noDays'); +is($date, $dateexpiry, 'date expiry with useDaysMode to noDays'); # Let's add a special holiday on 2013-01-01. With ReturnBeforeExpiry and # useDaysMode different from 'Days', return should forward the dateexpiry. -my $calendar = C4::Calendar->new(branchcode => $branchcode); -$calendar->insert_single_holiday( - day => 1, - month => 1, - year => 2013, - title =>'holidayTest', - description => 'holidayDesc' -); +my $calendar = Koha::DiscreteCalendar->new({branchcode => $branchcode}); +$calendar->edit_holiday({ + title => 'holidayTest', + holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION}, + start_date => dt_from_string->add(days=>30), + end_date => dt_from_string->add(days=>30) +}); $date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower ); -is($date, '2012-12-31T23:59:00', 'date expiry should be 2013-01-01 -1 day'); -$calendar->insert_single_holiday( - day => 31, - month => 12, - year => 2012, - title =>'holidayTest', - description => 'holidayDesc' -); +is($date, dt_from_string->truncate(to => 'day')->add(days=>29, hours=>23, minutes=>59), 'date expiry should be 2013-01-01 -1 day'); + +$calendar->edit_holiday({ + title => 'holidayTest', + holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION}, + start_date => dt_from_string->add(days => 29), + end_date => dt_from_string->add(days => 29) +}); $date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower ); -is($date, '2012-12-30T23:59:00', 'date expiry should be 2013-01-01 -2 day'); +is($date, dt_from_string->truncate(to => 'day')->add(days=> 28, hours=>23, minutes=>59), 'date expiry should be 2013-01-01 -2 day'); $date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower, 1 ); --- a/t/db_dependent/DiscreteCalendar.t +++ a/t/db_dependent/DiscreteCalendar.t @@ -18,7 +18,7 @@ # along with Koha; if not, see . use Modern::Perl; -use Test::More tests => 47; +use Test::More tests => 50; use Test::MockModule; use C4::Context; @@ -53,8 +53,8 @@ isnt($branch1,'', "First branch to do tests. BranchCode => $branch1"); isnt($branch2,'', "Second branch to do tests. BranchCode => $branch2"); #2 Calendars to make sure branches are treated separately. -my $calendar = Koha::DiscreteCalendar->new(branchcode => $branch1); -my $calendar2 = Koha::DiscreteCalendar->new(branchcode => $branch2); +my $calendar = Koha::DiscreteCalendar->new({branchcode => $branch1}); +my $calendar2 = Koha::DiscreteCalendar->new({branchcode => $branch2}); my $unique_holiday = DateTime->today; $calendar->edit_holiday({ @@ -68,6 +68,25 @@ is($calendar->is_opened($unique_holiday), 0, "Branch closed today : $unique_holi my @unique_holidays = $calendar->get_unique_holidays(); is(scalar @unique_holidays, 1, "Set of exception holidays at 1"); +my $yesterday = DateTime->today->subtract(days => 1); +$calendar->edit_holiday({ + title => "Single holiday Today", + holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION}, + start_date => $yesterday, + end_date => $yesterday +}); +is($calendar->is_opened($yesterday), 1, "Cannot edit dates in the past without override : $yesterday is not a holiday"); + +$calendar->edit_holiday({ + title => "Single holiday Today", + holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION}, + start_date => $yesterday, + end_date => $yesterday, + override => 1 +}); +is($calendar->is_opened($yesterday), 0, "Can edit dates in the past without override : $yesterday is a holiday"); + + my $days_between_start = DateTime->today; my $days_between_end = DateTime->today->add(days => 6); my $days_between = $calendar->days_between($days_between_start, $days_between_end)->in_units('days'); @@ -108,7 +127,7 @@ $calendar->edit_holiday({ end_date=>$unique_holiday_range_end }); @unique_holidays = $calendar->get_unique_holidays(); -is(scalar @unique_holidays, 7, "Set of exception holidays at 7"); +is(scalar @unique_holidays, 8, "Set of exception holidays at 7"); my $repeatable_holiday_range_start = DateTime->today->add(days => 8); my $repeatable_holiday_range_end = DateTime->today->add(days => 13); @@ -126,12 +145,12 @@ is(scalar @repeatable_holidays, 7, "Set of repeatable holidays at 7"); # item due : 2017-01-24 11:00:00 # item returned : 2017-01-26 10:00:00 # Branch closed : 2017-01-25 -# Open/close hours : 8AM to 4PM (8h day) +# Open/close hours : 08:00 to 16:00 (8h day) # Hours due : 5 hours on 2017-01-24 + 2 hours on 2017-01-26 = 7hours -my $open_hours_since_start = DateTime->today->add(days => 40, hours => 11); -my $open_hours_since_end = DateTime->today->add(days => 42, hours => 10); -my $holiday_between = DateTime->today->add(days => 41); +my $open_hours_since_start = dt_from_string->truncate(to => 'day')->add(days => 40, hours => 11); +my $open_hours_since_end = dt_from_string->truncate(to => 'day')->add(days => 42, hours => 10); +my $holiday_between = dt_from_string->truncate(to => 'day')->add(days => 41); $calendar->edit_holiday({ title => '', holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{NONE}, @@ -199,6 +218,15 @@ $calendar->edit_holiday({ }); is($calendar->is_opened($today), 1, "Today's holiday was removed"); +$calendar->edit_holiday({ + title => '', + holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{NONE}, + start_date => $yesterday, + end_date => $yesterday, + override => 1 +}); +is($calendar->is_opened($yesterday), 1, "Yesterdays's holiday was removed with override"); + my $new_open_hours = '08:00'; $calendar->edit_holiday({ title => '', --- a/t/db_dependent/Hold.t +++ a/t/db_dependent/Hold.t @@ -22,7 +22,7 @@ use C4::Context; use C4::Biblio qw( AddBiblio ); use Koha::Database; use Koha::Libraries; -use C4::Calendar; +use Koha::DiscreteCalendar; use Koha::Patrons; use Koha::Holds; use Koha::Item; @@ -67,7 +67,7 @@ my $hold = Koha::Hold->new( { biblionumber => $biblionumber, itemnumber => $item->id(), - reservedate => '2017-01-01', + reservedate => dt_from_string->subtract(days => 2), waitingdate => '2000-01-01', borrowernumber => $borrower->{borrowernumber}, branchcode => $branches[1]->{branchcode}, @@ -76,11 +76,19 @@ my $hold = Koha::Hold->new( ); $hold->store(); -my $b1_cal = C4::Calendar->new( branchcode => $branches[1]->{branchcode} ); -$b1_cal->insert_single_holiday( day => 02, month => 01, year => 2017, title => "Morty Day", description => "Rick" ); #Add a holiday +my $b1_cal = Koha::DiscreteCalendar->new({ branchcode => $branches[1]->{branchcode} }); +my $holiday = dt_from_string->subtract(days => 1); +$b1_cal->edit_holiday({ + title => "Morty Day", + holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION}, + start_date => $holiday, + end_date => $holiday, + override => 1 +}); #Add a holiday + my $today = dt_from_string; -is( $hold->age(), $today->delta_days( dt_from_string( '2017-01-01' ) )->in_units( 'days') , "Age of hold is days from reservedate to now if calendar ignored"); -is( $hold->age(1), $today->delta_days( dt_from_string( '2017-01-01' ) )->in_units( 'days' ) - 1 , "Age of hold is days from reservedate to now minus 1 if calendar used"); +is( $hold->age(), $today->delta_days( dt_from_string->subtract(days => 2) )->in_units( 'days' ), "Age of hold is days from reservedate to now if calendar ignored"); +is( $hold->age(1), $today->delta_days( dt_from_string->subtract(days => 2) )->in_units( 'days' ) - 1, "Age of hold is days from reservedate to now minus 1 if calendar used"); is( $hold->suspend, 0, "Hold is not suspended" ); $hold->suspend_hold(); --- a/t/db_dependent/Holds.t +++ a/t/db_dependent/Holds.t @@ -13,7 +13,6 @@ use Koha::Patrons; use C4::Items; use C4::Biblio; use C4::Reserves; -use C4::Calendar; use Koha::Database; use Koha::DateUtils qw( dt_from_string output_pref ); --- a/t/db_dependent/HoldsQueue.t +++ a/t/db_dependent/HoldsQueue.t @@ -200,9 +200,9 @@ $schema->resultset('DiscreteCalendar')->search({ close_hour => '16:00:00' }); -Koha::DiscreteCalendar->new( branchcode => '' )->add_new_branch('', $library1->{branchcode}); -Koha::DiscreteCalendar->new( branchcode => '' )->add_new_branch('', $library2->{branchcode}); -Koha::DiscreteCalendar->new( branchcode => '' )->add_new_branch('', $library3->{branchcode}); +Koha::DiscreteCalendar->new({ branchcode => '' })->add_new_branch('', $library1->{branchcode}); +Koha::DiscreteCalendar->new({ branchcode => '' })->add_new_branch('', $library2->{branchcode}); +Koha::DiscreteCalendar->new({ branchcode => '' })->add_new_branch('', $library3->{branchcode}); @branchcodes = ( $library1->{branchcode}, $library2->{branchcode}, $library3->{branchcode} ); @@ -320,14 +320,14 @@ my $today = dt_from_string(); # If the test below is removed, aother tests using the holiday will fail. For some reason if we call is_holiday now # the holiday will get set in cache correctly, but not if we let C4::HoldsQueue call is_holiday instead. -Koha::DiscreteCalendar->new( branchcode => $branchcodes[0] )->edit_holiday({ +Koha::DiscreteCalendar->new({ branchcode => $branchcodes[0] })->edit_holiday({ title => "Today", - holiday_type => "E", + holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION}, start_date => $today, end_date => $today }); -is( Koha::DiscreteCalendar->new( branchcode => $branchcodes[0] )->is_holiday( $today ), 1, 'Is today a holiday for pickup branch' ); +is( Koha::DiscreteCalendar->new({ branchcode => $branchcodes[0] })->is_holiday( $today ), 1, 'Is today a holiday for pickup branch' ); C4::HoldsQueue::CreateQueue(); $holds_queue = $dbh->selectall_arrayref("SELECT * FROM tmp_holdsqueue", { Slice => {} }); is( scalar( @$holds_queue ), 1, "Holds not filled with items from closed libraries" ); --- a/tools/discrete_calendar.pl +++ a/tools/discrete_calendar.pl @@ -16,8 +16,7 @@ # along with Koha; if not, see . #####Sets holiday periods for each branch. Datedues will be extended if branch is closed -TG -use strict; -use warnings; +use Modern::Perl; use CGI qw ( -utf8 ); @@ -40,7 +39,7 @@ my ($template, $loggedinuser, $cookie) }); my $branch = $input->param('branch') || C4::Context->userenv->{'branch'}; -my $calendar = Koha::DiscreteCalendar->new(branchcode => $branch); +my $calendar = Koha::DiscreteCalendar->new({branchcode => $branch}); #alert the user that they are using the default calendar because they do not have a library set my $no_branch_selected = $calendar->{no_branch_selected}; @@ -89,6 +88,8 @@ if($action eq 'copyBranch'){ $endDate = $startDate->clone(); } + warn $startDate; + warn $endDate; $calendar->edit_holiday( { title => $title, weekday => $weekday, --