From 78e9f8634a0d2aa031aad156baf117b70ca01b05 Mon Sep 17 00:00:00 2001
From: Charles Farmer <charles.farmer@inLibro.com>
Date: Wed, 4 Sep 2019 14:56:01 -0400
Subject: [PATCH] Bug 17015: DiscreteCalendar UI, Back-End and necessary
scripts
---
C4/Calendar.pm | 736 ---------
C4/Circulation.pm | 11 +-
C4/HoldsQueue.pm | 8 +-
C4/Overdues.pm | 5 +-
C4/Reserves.pm | 4 +-
Koha/Calendar.pm | 555 -------
Koha/DiscreteCalendar.pm | 1317 +++++++++++++++++
Koha/Hold.pm | 6 +-
Koha/Schema/Result/DiscreteCalendar.pm | 111 ++
circ/returns.pl | 3 +-
.../prog/css/discretecalendar.css | 206 +++
.../prog/en/includes/tools-menu.inc | 2 +-
.../en/modules/tools/discrete_calendar.tt | 678 +++++++++
.../prog/en/modules/tools/tools-home.tt | 2 +-
misc/cronjobs/add_days_discrete_calendar.pl | 128 ++
misc/cronjobs/fines.pl | 4 +-
misc/cronjobs/holds/cancel_unfilled_holds.pl | 2 +-
misc/cronjobs/overdue_notices.pl | 18 +-
misc/cronjobs/staticfines.pl | 4 +-
.../thirdparty/TalkingTech_itiva_outbound.pl | 4 +-
tools/copy-holidays.pl | 40 -
tools/discrete_calendar.pl | 155 ++
tools/exceptionHolidays.pl | 143 --
tools/holidays.pl | 131 --
tools/newHolidays.pl | 144 --
25 files changed, 2630 insertions(+), 1787 deletions(-)
delete mode 100644 C4/Calendar.pm
delete mode 100644 Koha/Calendar.pm
create mode 100644 Koha/DiscreteCalendar.pm
create mode 100644 Koha/Schema/Result/DiscreteCalendar.pm
create mode 100644 koha-tmpl/intranet-tmpl/prog/css/discretecalendar.css
create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/tools/discrete_calendar.tt
create mode 100755 misc/cronjobs/add_days_discrete_calendar.pl
delete mode 100755 tools/copy-holidays.pl
create mode 100755 tools/discrete_calendar.pl
delete mode 100755 tools/exceptionHolidays.pl
delete mode 100755 tools/holidays.pl
delete mode 100755 tools/newHolidays.pl
diff --git a/C4/Calendar.pm b/C4/Calendar.pm
deleted file mode 100644
index 6a5d8a79a4..0000000000
--- a/C4/Calendar.pm
+++ /dev/null
@@ -1,736 +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 <http://www.gnu.org/licenses>.
-
-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();
- my $key = $self->{branchcode} . "_holidays";
- $cache->clear_from_cache($key);
-
- 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();
- my $key = $self->{branchcode} . "_holidays";
- $cache->clear_from_cache($key);
-
- 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();
- my $key = $self->{branchcode} . "_holidays";
- $cache->clear_from_cache($key);
-
- 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();
- my $key = $self->{branchcode} . "_holidays";
- $cache->clear_from_cache($key);
-
- 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();
- my $key = $self->{branchcode} . "_holidays";
- $cache->clear_from_cache($key);
-
- 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();
- my $key = $self->{branchcode} . "_holidays";
- $cache->clear_from_cache($key);
-
-}
-
-=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();
- my $key = $self->{branchcode} . "_holidays";
- $cache->clear_from_cache($key);
-}
-
-=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;
- my $target_wdh = $target_calendar->get_week_days_holidays;
- foreach my $key (keys %$wdh) {
- unless (grep { $_ eq $key } keys %$target_wdh) {
- $target_calendar->insert_week_day_holiday( weekday => $key, %{ $wdh->{$key} } )
- }
- }
-
- my $dmh = $self->get_day_month_holidays;
- my $target_dmh = $target_calendar->get_day_month_holidays;
- foreach my $values (values %$dmh) {
- unless (grep { $_->{day} eq $values->{day} && $_->{month} eq $values->{month} } values %$target_dmh) {
- $target_calendar->insert_day_month_holiday(%{ $values });
- }
- }
-
- my $exception_holidays = $self->get_exception_holidays;
- my $target_exceptions = $target_calendar->get_exception_holidays;
- foreach my $values ( grep {$_->{date} gt $today} values %{ $exception_holidays }) {
- unless ( grep { $_->{date} eq $values->{date} } values %$target_exceptions) {
- $target_calendar->insert_exception_holiday(%{ $values });
- }
- }
-
- my $single_holidays = $self->get_single_holidays;
- my $target_singles = $target_calendar->get_single_holidays;
- foreach my $values ( grep {$_->{date} gt $today} values %{ $single_holidays }) {
- unless ( grep { $_->{date} eq $values->{date} } values %$target_singles){
- $target_calendar->insert_single_holiday(%{ $values });
- }
- }
-
- return 1;
-}
-
-1;
-
-__END__
-
-=head1 AUTHOR
-
-Koha Physics Library UNLP <matias_veleda@hotmail.com>
-
-=cut
-
diff --git a/C4/Circulation.pm b/C4/Circulation.pm
index 3571051e84..eb229ea222 100644
--- a/C4/Circulation.pm
+++ b/C4/Circulation.pm
@@ -42,7 +42,7 @@ use Koha::Account;
use Koha::AuthorisedValues;
use Koha::Biblioitems;
use Koha::DateUtils;
-use Koha::Calendar;
+use Koha::DiscreteCalendar;
use Koha::Checkouts;
use Koha::Illrequests;
use Koha::Items;
@@ -1302,7 +1302,7 @@ sub checkHighHolds {
branchcode => $branchcode,
}
);
- my $calendar = Koha::Calendar->new( branchcode => $branchcode, days_mode => $daysmode );
+ my $calendar = Koha::DiscreteCalendar->new( branchcode => $branchcode, days_mode => $daysmode );
my $orig_due = C4::Circulation::CalcDateDue( $issuedate, $itype, $branchcode, $borrower );
@@ -3573,7 +3573,7 @@ sub CalcDateDue {
else { # days
$dur = DateTime::Duration->new( days => $loanlength->{$length_key});
}
- my $calendar = Koha::Calendar->new( branchcode => $branch, days_mode => $daysmode );
+ my $calendar = Koha::DiscreteCalendar->new( branchcode => $branch, days_mode => $daysmode );
$datedue = $calendar->addDate( $datedue, $dur, $loanlength->{lengthunit} );
if ($loanlength->{lengthunit} eq 'days') {
$datedue->set_hour(23);
@@ -3612,14 +3612,13 @@ sub CalcDateDue {
}
}
if ( $daysmode ne 'Days' ) {
- my $calendar = Koha::Calendar->new( branchcode => $branch, days_mode => $daysmode );
+ my $calendar = Koha::DiscreteCalendar->new( branchcode => $branch, days_mode => $daysmode );
if ( $calendar->is_holiday($datedue) ) {
# Don't return on a closed day
- $datedue = $calendar->prev_open_days( $datedue, 1 );
+ $datedue = $calendar->prev_open_days( $datedue )->set(hour => 23, minute => 59);
}
}
}
-
return $datedue;
}
diff --git a/C4/HoldsQueue.pm b/C4/HoldsQueue.pm
index b891afc437..a929c13afc 100644
--- a/C4/HoldsQueue.pm
+++ b/C4/HoldsQueue.pm
@@ -31,7 +31,7 @@ use C4::Biblio;
use Koha::DateUtils;
use Koha::Items;
use Koha::Patrons;
-
+use Koha::DiscreteCalendar;
use List::Util qw(shuffle);
use List::MoreUtils qw(any);
use Data::Dumper;
@@ -78,7 +78,7 @@ sub TransportCostMatrix {
};
if ( C4::Context->preference("HoldsQueueSkipClosed") ) {
- $calendars->{$from} ||= Koha::Calendar->new( branchcode => $from );
+ $calendars->{$from} ||= Koha::DiscreteCalendar->new({ branchcode => $from });
$transport_cost_matrix{$to}{$from}{disable_transfer} ||=
$calendars->{$from}->is_holiday( $today );
}
@@ -205,7 +205,7 @@ sub CreateQueue {
$total_requests += scalar(@$hold_requests);
$total_available_items += scalar(@$available_items);
- my $item_map = MapItemsToHoldRequests($hold_requests, $available_items, $branches_to_use, $transport_cost_matrix);
+ my $item_map = MapItemsToHoldRequests($hold_requests, $available_items, $branches_to_use, $transport_cost_matrix);
$item_map or next;
my $item_map_size = scalar(keys %$item_map)
or next;
@@ -762,7 +762,7 @@ sub load_branches_to_pull_from {
my $today = dt_from_string();
if ( C4::Context->preference('HoldsQueueSkipClosed') ) {
@branches_to_use = grep {
- !Koha::Calendar->new( branchcode => $_ )
+ !Koha::DiscreteCalendar->new({ branchcode => $_ })
->is_holiday( $today )
} @branches_to_use;
}
diff --git a/C4/Overdues.pm b/C4/Overdues.pm
index a9de67d1ee..9cc6162342 100644
--- a/C4/Overdues.pm
+++ b/C4/Overdues.pm
@@ -33,6 +33,7 @@ use C4::Accounts;
use C4::Log; # logaction
use C4::Debug;
use Koha::DateUtils;
+use Koha::DiscreteCalendar;
use Koha::Account::Lines;
use Koha::Account::Offsets;
use Koha::Libraries;
@@ -304,7 +305,7 @@ sub get_chargeable_units {
my $charge_duration;
if ($unit eq 'hours') {
if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
- my $calendar = Koha::Calendar->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 );
@@ -316,7 +317,7 @@ sub get_chargeable_units {
}
else { # days
if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
- my $calendar = Koha::Calendar->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 );
diff --git a/C4/Reserves.pm b/C4/Reserves.pm
index 2422d4facd..decbc0e427 100644
--- a/C4/Reserves.pm
+++ b/C4/Reserves.pm
@@ -34,10 +34,10 @@ use C4::Members::Messaging;
use C4::Members;
use Koha::Account::Lines;
use Koha::Biblios;
-use Koha::Calendar;
use Koha::CirculationRules;
use Koha::Database;
use Koha::DateUtils;
+use Koha::DiscreteCalendar;
use Koha::Hold;
use Koha::Holds;
use Koha::ItemTypes;
@@ -913,7 +913,7 @@ sub CancelExpiredReserves {
my $holds = Koha::Holds->search( $params );
while ( my $hold = $holds->next ) {
- my $calendar = Koha::Calendar->new( branchcode => $hold->branchcode );
+ my $calendar = Koha::DiscreteCalendar->new({ branchcode => $hold->branchcode });
next if !$cancel_on_holidays && $calendar->is_holiday( $today );
diff --git a/Koha/Calendar.pm b/Koha/Calendar.pm
deleted file mode 100644
index c3210d469d..0000000000
--- a/Koha/Calendar.pm
+++ /dev/null
@@ -1,555 +0,0 @@
-package Koha::Calendar;
-
-use Modern::Perl;
-
-use Carp;
-use DateTime;
-use DateTime::Duration;
-use C4::Context;
-use Koha::Caches;
-use Koha::Exceptions;
-
-sub new {
- my ( $classname, %options ) = @_;
- my $self = {};
- bless $self, $classname;
- for my $o_name ( keys %options ) {
- my $o = lc $o_name;
- $self->{$o} = $options{$o_name};
- }
- if ( !defined $self->{branchcode} ) {
- croak 'No branchcode argument passed to Koha::Calendar->new';
- }
- $self->_init();
- return $self;
-}
-
-sub _init {
- my $self = shift;
- my $branch = $self->{branchcode};
- my $dbh = C4::Context->dbh();
- my $weekly_closed_days_sth = $dbh->prepare(
-'SELECT weekday FROM repeatable_holidays WHERE branchcode = ? AND weekday IS NOT NULL'
- );
- $weekly_closed_days_sth->execute( $branch );
- $self->{weekly_closed_days} = [ 0, 0, 0, 0, 0, 0, 0 ];
- while ( my $tuple = $weekly_closed_days_sth->fetchrow_hashref ) {
- $self->{weekly_closed_days}->[ $tuple->{weekday} ] = 1;
- }
- my $day_month_closed_days_sth = $dbh->prepare(
-'SELECT day, month FROM repeatable_holidays WHERE branchcode = ? AND weekday IS NULL'
- );
- $day_month_closed_days_sth->execute( $branch );
- $self->{day_month_closed_days} = {};
- while ( my $tuple = $day_month_closed_days_sth->fetchrow_hashref ) {
- $self->{day_month_closed_days}->{ $tuple->{month} }->{ $tuple->{day} } =
- 1;
- }
-
- $self->{test} = 0;
- return;
-}
-
-sub _holidays {
- my ($self) = @_;
-
- my $key = $self->{branchcode} . "_holidays";
- my $cache = Koha::Caches->get_instance();
- my $holidays = $cache->get_from_cache($key);
-
- # $holidays looks like:
- # {
- # 20131122 => 1, # single_holiday
- # 20131123 => 0, # exception_holiday
- # ...
- # }
-
- # Populate the cache if necessary
- unless ($holidays) {
- my $dbh = C4::Context->dbh;
- $holidays = {};
-
- # Add holidays for each branch
- my $holidays_sth = $dbh->prepare(
-'SELECT day, month, year, MAX(isexception) FROM special_holidays WHERE branchcode = ? GROUP BY day, month, year'
- );
- $holidays_sth->execute($self->{branchcode});
-
- while ( my ( $day, $month, $year, $exception ) =
- $holidays_sth->fetchrow )
- {
- my $datestring =
- sprintf( "%04d", $year )
- . sprintf( "%02d", $month )
- . sprintf( "%02d", $day );
-
- $holidays->{$datestring} = $exception ? 0 : 1;
- }
- $cache->set_in_cache( $key, $holidays, { expiry => 76800 } );
- }
- return $holidays // {};
-}
-
-sub addDate {
- my ( $self, $startdate, $add_duration, $unit ) = @_;
-
-
- Koha::Exceptions::MissingParameter->throw("Missing mandatory option for Koha:Calendar->addDate: days_mode")
- unless exists $self->{days_mode};
-
- # Default to days duration (legacy support I guess)
- if ( ref $add_duration ne 'DateTime::Duration' ) {
- $add_duration = DateTime::Duration->new( days => $add_duration );
- }
-
- $unit ||= 'days'; # default days ?
- my $dt;
- if ( $unit eq 'hours' ) {
- # Fixed for legacy support. Should be set as a branch parameter
- my $return_by_hour = 10;
-
- $dt = $self->addHours($startdate, $add_duration, $return_by_hour);
- } else {
- # days
- $dt = $self->addDays($startdate, $add_duration);
- }
- return $dt;
-}
-
-sub addHours {
- my ( $self, $startdate, $hours_duration, $return_by_hour ) = @_;
- my $base_date = $startdate->clone();
-
- $base_date->add_duration($hours_duration);
-
- # If we are using the calendar behave for now as if Datedue
- # was the chosen option (current intended behaviour)
-
- Koha::Exceptions::MissingParameter->throw("Missing mandatory option for Koha:Calendar->addHours: days_mode")
- unless exists $self->{days_mode};
-
- if ( $self->{days_mode} ne 'Days' &&
- $self->is_holiday($base_date) ) {
-
- if ( $hours_duration->is_negative() ) {
- $base_date = $self->prev_open_days($base_date, 1);
- } else {
- $base_date = $self->next_open_days($base_date, 1);
- }
-
- $base_date->set_hour($return_by_hour);
-
- }
-
- return $base_date;
-}
-
-sub addDays {
- my ( $self, $startdate, $days_duration ) = @_;
- my $base_date = $startdate->clone();
-
- Koha::Exceptions::MissingParameter->throw("Missing mandatory option for Koha:Calendar->addDays: days_mode")
- unless exists $self->{days_mode};
-
- if ( $self->{days_mode} eq 'Calendar' ) {
- # use the calendar to skip all days the library is closed
- # when adding
- my $days = abs $days_duration->in_units('days');
-
- if ( $days_duration->is_negative() ) {
- while ($days) {
- $base_date = $self->prev_open_days($base_date, 1);
- --$days;
- }
- } else {
- while ($days) {
- $base_date = $self->next_open_days($base_date, 1);
- --$days;
- }
- }
-
- } else { # Days, Datedue or Dayweek
- # use straight days, then use calendar to push
- # the date to the next open day as appropriate
- # if Datedue or Dayweek
- $base_date->add_duration($days_duration);
-
- if ( $self->{days_mode} eq 'Datedue' ||
- $self->{days_mode} eq 'Dayweek') {
- # Datedue or Dayweek, then use the calendar to push
- # the date to the next open day if holiday
- if ( $self->is_holiday($base_date) ) {
- my $dow = $base_date->day_of_week;
- my $days = $days_duration->in_units('days');
- # Is it a period based on weeks
- my $push_amt = $days % 7 == 0 ?
- $self->get_push_amt($base_date) : 1;
- if ( $days_duration->is_negative() ) {
- $base_date = $self->prev_open_days($base_date, $push_amt);
- } else {
- $base_date = $self->next_open_days($base_date, $push_amt);
- }
- }
- }
- }
-
- return $base_date;
-}
-
-sub get_push_amt {
- my ( $self, $base_date) = @_;
-
- Koha::Exceptions::MissingParameter->throw("Missing mandatory option for Koha:Calendar->get_push_amt: days_mode")
- unless exists $self->{days_mode};
-
- my $dow = $base_date->day_of_week;
- # Representation fix
- # DateTime object dow (1-7) where Monday is 1
- # Arrays are 0-based where 0 = Sunday, not 7.
- if ( $dow == 7 ) {
- $dow = 0;
- }
-
- return (
- # We're using Dayweek useDaysMode option
- $self->{days_mode} eq 'Dayweek' &&
- # It's not a permanently closed day
- !$self->{weekly_closed_days}->[$dow]
- ) ? 7 : 1;
-}
-
-sub is_holiday {
- my ( $self, $dt ) = @_;
-
- my $localdt = $dt->clone();
- my $day = $localdt->day;
- my $month = $localdt->month;
- my $ymd = $localdt->ymd('');
-
- #Change timezone to "floating" before doing any calculations or comparisons
- $localdt->set_time_zone("floating");
- $localdt->truncate( to => 'day' );
-
- return $self->_holidays->{$ymd} if defined($self->_holidays->{$ymd});
-
- my $dow = $localdt->day_of_week;
- # Representation fix
- # DateTime object dow (1-7) where Monday is 1
- # Arrays are 0-based where 0 = Sunday, not 7.
- if ( $dow == 7 ) {
- $dow = 0;
- }
-
- if ( $self->{weekly_closed_days}->[$dow] == 1 ) {
- return 1;
- }
-
- if ( exists $self->{day_month_closed_days}->{$month}->{$day} ) {
- return 1;
- }
-
- # damn have to go to work after all
- return 0;
-}
-
-sub next_open_days {
- my ( $self, $dt, $to_add ) = @_;
-
- Koha::Exceptions::MissingParameter->throw("Missing mandatory option for Koha:Calendar->next_open_days: days_mode")
- unless exists $self->{days_mode};
-
- my $base_date = $dt->clone();
-
- $base_date->add(days => $to_add);
- while ($self->is_holiday($base_date)) {
- my $add_next = $self->get_push_amt($base_date);
- $base_date->add(days => $add_next);
- }
- return $base_date;
-}
-
-sub prev_open_days {
- my ( $self, $dt, $to_sub ) = @_;
-
- Koha::Exceptions::MissingParameter->throw("Missing mandatory option for Koha:Calendar->get_open_days: days_mode")
- unless exists $self->{days_mode};
-
- my $base_date = $dt->clone();
-
- # It feels logical to be passed a positive number, though we're
- # subtracting, so do the right thing
- $to_sub = $to_sub > 0 ? 0 - $to_sub : $to_sub;
-
- $base_date->add(days => $to_sub);
-
- while ($self->is_holiday($base_date)) {
- my $sub_next = $self->get_push_amt($base_date);
- # Ensure we're subtracting when we need to be
- $sub_next = $sub_next > 0 ? 0 - $sub_next : $sub_next;
- $base_date->add(days => $sub_next);
- }
-
- return $base_date;
-}
-
-sub days_forward {
- my $self = shift;
- my $start_dt = shift;
- my $num_days = shift;
-
- Koha::Exceptions::MissingParameter->throw("Missing mandatory option for Koha:Calendar->days_forward: days_mode")
- unless exists $self->{days_mode};
-
- return $start_dt unless $num_days > 0;
-
- my $base_dt = $start_dt->clone();
-
- while ($num_days--) {
- $base_dt = $self->next_open_days($base_dt, 1);
- }
-
- return $base_dt;
-}
-
-sub days_between {
- my $self = shift;
- my $start_dt = shift;
- my $end_dt = shift;
-
- # Change time zone for date math and swap if needed
- $start_dt = $start_dt->clone->set_time_zone('floating');
- $end_dt = $end_dt->clone->set_time_zone('floating');
- if( $start_dt->compare($end_dt) > 0 ) {
- ( $start_dt, $end_dt ) = ( $end_dt, $start_dt );
- }
-
- # start and end should not be closed days
- my $delta_days = $start_dt->delta_days($end_dt)->delta_days;
- while( $start_dt->compare($end_dt) < 1 ) {
- $delta_days-- if $self->is_holiday($start_dt);
- $start_dt->add( days => 1 );
- }
- return DateTime::Duration->new( days => $delta_days );
-}
-
-sub hours_between {
- my ($self, $start_date, $end_date) = @_;
- my $start_dt = $start_date->clone()->set_time_zone('floating');
- my $end_dt = $end_date->clone()->set_time_zone('floating');
-
- my $duration = $end_dt->delta_ms($start_dt);
- $start_dt->truncate( to => 'day' );
- $end_dt->truncate( to => 'day' );
-
- # NB this is a kludge in that it assumes all days are 24 hours
- # However for hourly loans the logic should be expanded to
- # take into account open/close times then it would be a duration
- # of library open hours
- my $skipped_days = 0;
- while( $start_dt->compare($end_dt) < 1 ) {
- $skipped_days++ if $self->is_holiday($start_dt);
- $start_dt->add( days => 1 );
- }
-
- if ($skipped_days) {
- $duration->subtract_duration(DateTime::Duration->new( hours => 24 * $skipped_days));
- }
-
- return $duration;
-}
-
-sub set_daysmode {
- my ( $self, $mode ) = @_;
-
- # if not testing this is a no op
- if ( $self->{test} ) {
- $self->{days_mode} = $mode;
- }
-
- return;
-}
-
-sub clear_weekly_closed_days {
- my $self = shift;
- $self->{weekly_closed_days} = [ 0, 0, 0, 0, 0, 0, 0 ]; # Sunday only
- return;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-Koha::Calendar - Object containing a branches calendar
-
-=head1 SYNOPSIS
-
- use Koha::Calendar
-
- my $c = Koha::Calendar->new( branchcode => 'MAIN' );
- my $dt = dt_from_string();
-
- # are we open
- $open = $c->is_holiday($dt);
- # when will item be due if loan period = $dur (a DateTime::Duration object)
- $duedate = $c->addDate($dt,$dur,'days');
-
-
-=head1 DESCRIPTION
-
- Implements those features of C4::Calendar needed for Staffs Rolling Loans
-
-=head1 METHODS
-
-=head2 new : Create a calendar object
-
-my $calendar = Koha::Calendar->new( branchcode => 'MAIN' );
-
-The option branchcode is required
-
-
-=head2 addDate
-
- my $dt = $calendar->addDate($date, $dur, $unit)
-
-C<$date> is a DateTime object representing the starting date of the interval.
-
-C<$offset> is a DateTime::Duration to add to it
-
-C<$unit> is a string value 'days' or 'hours' toflag granularity of duration
-
-Currently unit is only used to invoke Staffs return Monday at 10 am rule this
-parameter will be removed when issuingrules properly cope with that
-
-
-=head2 addHours
-
- my $dt = $calendar->addHours($date, $dur, $return_by_hour )
-
-C<$date> is a DateTime object representing the starting date of the interval.
-
-C<$offset> is a DateTime::Duration to add to it
-
-C<$return_by_hour> is an integer value representing the opening hour for the branch
-
-=head2 get_push_amt
-
- my $amt = $calendar->get_push_amt($date)
-
-C<$date> is a DateTime object representing a closed return date
-
-Using the days_mode syspref value and the nature of the closed return
-date, return how many days we should jump forward to find another return date
-
-=head2 addDays
-
- my $dt = $calendar->addDays($date, $dur)
-
-C<$date> is a DateTime object representing the starting date of the interval.
-
-C<$offset> is a DateTime::Duration to add to it
-
-C<$unit> is a string value 'days' or 'hours' toflag granularity of duration
-
-Currently unit is only used to invoke Staffs return Monday at 10 am rule this
-parameter will be removed when issuingrules properly cope with that
-
-=head2 is_holiday
-
-$yesno = $calendar->is_holiday($dt);
-
-passed a DateTime object returns 1 if it is a closed day
-0 if not according to the calendar
-
-=head2 days_between
-
-$duration = $calendar->days_between($start_dt, $end_dt);
-
-Passed two dates returns a DateTime::Duration object measuring the length between them
-ignoring closed days. Always returns a positive number irrespective of the
-relative order of the parameters.
-
-Note: This routine assumes neither the passed start_dt nor end_dt can be a closed day
-
-=head2 hours_between
-
-$duration = $calendar->hours_between($start_dt, $end_dt);
-
-Passed two dates returns a DateTime::Duration object measuring the length between them
-ignoring closed days. Always returns a positive number irrespective of the
-relative order of the parameters.
-
-Note: This routine assumes neither the passed start_dt nor end_dt can be a closed day
-
-=head2 next_open_days
-
-$datetime = $calendar->next_open_days($duedate_dt, $to_add)
-
-Passed a Datetime and number of days, returns another Datetime representing
-the next open day after adding the passed number of days. It is intended for
-use to calculate the due date when useDaysMode syspref is set to either
-'Datedue', 'Calendar' or 'Dayweek'.
-
-=head2 prev_open_days
-
-$datetime = $calendar->prev_open_days($duedate_dt, $to_sub)
-
-Passed a Datetime and a number of days, returns another Datetime
-representing the previous open day after subtracting the number of passed
-days. It is intended for use to calculate the due date when useDaysMode
-syspref is set to either 'Datedue', 'Calendar' or 'Dayweek'.
-
-=head2 days_forward
-
-$datetime = $calendar->days_forward($start_dt, $to_add)
-
-Passed a Datetime and number of days, returns another Datetime representing
-the next open day after adding the passed number of days. It is intended for
-use to calculate the due date when useDaysMode syspref is set to either
-'Datedue', 'Calendar' or 'Dayweek'.
-
-=head2 set_daysmode
-
-For testing only allows the calling script to change days mode
-
-=head2 clear_weekly_closed_days
-
-In test mode changes the testing set of closed days to a new set with
-no closed days. TODO passing an array of closed days to this would
-allow testing of more configurations
-
-=head2 add_holiday
-
-Passed a datetime object this will add it to the calendar's list of
-closed days. This is for testing so that we can alter the Calenfar object's
-list of specified dates
-
-=head1 DIAGNOSTICS
-
-Will croak if not passed a branchcode in new
-
-=head1 BUGS AND LIMITATIONS
-
-This only contains a limited subset of the functionality in C4::Calendar
-Only enough to support Staffs Rolling loans
-
-=head1 AUTHOR
-
-Colin Campbell colin.campbell@ptfs-europe.com
-
-=head1 LICENSE AND COPYRIGHT
-
-Copyright (c) 2011 PTFS-Europe Ltd All rights reserved
-
-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 <http://www.gnu.org/licenses>.
diff --git a/Koha/DiscreteCalendar.pm b/Koha/DiscreteCalendar.pm
new file mode 100644
index 0000000000..90682f2025
--- /dev/null
+++ b/Koha/DiscreteCalendar.pm
@@ -0,0 +1,1317 @@
+package Koha::DiscreteCalendar;
+
+# 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 <http://www.gnu.org/licenses>.
+
+#####Sets holiday periods for each branch. Datedues will be extended if branch is closed -TG
+use Modern::Perl;
+
+use CGI qw ( -utf8 );
+use Carp;
+use DateTime;
+use DateTime::Format::Strptime;
+use Data::Dumper;
+
+use C4::Context;
+use C4::Output;
+use Koha::Database;
+use Koha::DateUtils;
+
+# Global variables to make code more readable
+our $HOLIDAYS = {
+ EXCEPTION => 'E',
+ REPEATABLE => 'R',
+ SINGLE => 'S',
+ NEED_VALIDATION => 'N',
+ FLOAT => 'F',
+ WEEKLY => 'W',
+ NONE => 'none'
+};
+
+=head1 NAME
+
+Koha::DiscreteCalendar - Object containing a branches calendar, working with the SQL database
+
+=head1 SYNOPSIS
+
+ use Koha::DiscreteCalendar
+
+ my $c = Koha::DiscreteCalendar->new({ branchcode => 'MAIN' });
+ my $dt = DateTime->now();
+
+ # are we open
+ $open = $c->is_holiday($dt);
+ # when will item be due if loan period = $dur (a DateTime::Duration object)
+ $duedate = $c->addDate($dt,$dur,'days');
+
+
+=head1 DESCRIPTION
+
+ Implements a new Calendar object, but uses the SQL database to keep track of days and holidays.
+ This results in a performance gain since the optimization is done by the MySQL database/team.
+
+=head1 METHODS
+
+=head2 new : Create a (discrete) calendar object
+
+my $calendar = Koha::DiscreteCalendar->new({ branchcode => 'MAIN' });
+
+The option branchcode is required
+
+=cut
+
+sub new {
+ my ( $classname, $options ) = @_;
+ my $self = {};
+ bless $self, $classname;
+ for my $o_name ( keys %{ $options } ) {
+ my $o = lc $o_name;
+ $self->{$o} = $options->{$o_name};
+ }
+ if ( !defined $self->{branchcode} ) {
+ croak 'No branchcode argument passed to Koha::DiscreteCalendar->new';
+ }
+ $self->_init();
+
+ return $self;
+}
+
+sub _init {
+ my $self = shift;
+ $self->{days_mode} ||= C4::Context->preference('useDaysMode');
+ #If the branchcode doesn't exist we use the default calendar.
+ my $schema = Koha::Database->new->schema;
+ my $branchcode = $self->{branchcode};
+ my $dtf = $schema->storage->datetime_parser;
+ my $today = $dtf->format_datetime(DateTime->today);
+ my $rs = $schema->resultset('DiscreteCalendar')->single(
+ {
+ branchcode => $branchcode,
+ date => $today
+ }
+ );
+ #use default if no calendar is found
+ if (!$rs){
+ $self->{branchcode} = '';
+ $self->{no_branch_selected} = 1;
+ }
+
+}
+
+=head2 get_dates_info
+
+ my @dates = $calendar->get_dates_info();
+
+Returns an array of hashes representing the dates in this calendar. The hash
+contains the fields C<$date>, C<$outputdate>, C<$holiday_type>, C<$open_hour>,
+C<$close_hour> and C<$note>.
+
+=cut
+
+sub get_dates_info {
+ my $self = shift;
+ my $branchcode = $self->{branchcode};
+ my @datesInfos =();
+ my $schema = Koha::Database->new->schema;
+
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode
+ },
+ {
+ select => [ 'date', { DATE => 'date' } ],
+ as => [qw/ date date /],
+ columns =>[ qw/ holiday_type open_hour close_hour note/]
+ },
+ );
+
+ while (my $date = $rs->next()){
+ my $outputdate = dt_from_string( $date->date(), 'iso');
+ $outputdate = output_pref( { dt => $outputdate, dateonly => 1 } );
+ push @datesInfos, {
+ date => $date->date(),
+ outputdate => $outputdate,
+ holiday_type => $date->holiday_type() ,
+ open_hour => $date->open_hour(),
+ close_hour => $date->close_hour(),
+ note => $date->note()
+ };
+ }
+
+ return @datesInfos;
+}
+
+=head2 add_new_branch
+
+ Koha::DiscreteCalendar->add_new_branch($copyBranch, $newBranch)
+
+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
+
+=cut
+
+sub add_new_branch {
+ my ( $classname, $copyBranch, $newBranch) = @_;
+
+ $copyBranch = '' unless $copyBranch;
+ my $schema = Koha::Database->new->schema;
+
+ my $branch_rs = $schema->resultset('DiscreteCalendar')->search({
+ branchcode => $copyBranch
+ });
+
+ while(my $row = $branch_rs->next()){
+ $schema->resultset('DiscreteCalendar')->create({
+ date => $row->date(),
+ branchcode => $newBranch,
+ is_opened => $row->is_opened(),
+ holiday_type => $row->holiday_type(),
+ open_hour => $row->open_hour(),
+ close_hour => $row->close_hour(),
+ });
+ }
+
+}
+
+=head2 get_date_info
+
+ my $date = $calendar->get_date_info;
+
+Returns a reference-to-hash representing a DiscreteCalendar date data object.
+The hash contains the fields C<$date>, C<$outputdate>, C<$holiday_type>,
+C<$open_hour>, C<$close_hour> and C<$note>.
+
+=cut
+
+sub get_date_info {
+ my ($self, $date) = @_;
+ my $branchcode = $self->{branchcode};
+ my $schema = Koha::Database->new->schema;
+ my $dtf = $schema->storage->datetime_parser;
+ #String dates for Database usage
+ my $date_string = $dtf->format_datetime($date);
+
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ },
+ {
+ select => [ 'date', { DATE => 'date' } ],
+ as => [qw/ date date /],
+ where => \['DATE(?) = date', $date_string ],
+ columns =>[ qw/ branchcode holiday_type open_hour close_hour note/]
+ },
+ );
+ my $dateDTO;
+ while (my $date = $rs->next()){
+ $dateDTO = {
+ date => $date->date(),
+ branchcode => $date->branchcode(),
+ holiday_type => $date->holiday_type() ,
+ open_hour => $date->open_hour(),
+ close_hour => $date->close_hour(),
+ note => $date->note()
+ };
+ }
+
+ return $dateDTO;
+}
+
+=head2 get_max_date
+
+ my $maxDate = $calendar->get_max_date();
+
+Returns the furthest date available in the databse of current branch.
+
+=cut
+
+sub get_max_date {
+ my $self = shift;
+ my $branchcode = $self->{branchcode};
+ my $schema = Koha::Database->new->schema;
+
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode
+ },
+ {
+ select => [{ MAX => 'date' } ],
+ as => [qw/ max /],
+ }
+ );
+
+ return $rs->next()->get_column('max');
+}
+
+=head2 get_min_date
+
+ my $minDate = $calendar->get_min_date();
+
+Returns the oldest date available in the databse of current branch.
+
+=cut
+
+sub get_min_date {
+ my $self = shift;
+ my $branchcode = $self->{branchcode};
+ my $schema = Koha::Database->new->schema;
+
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode
+ },
+ {
+ select => [{ MIN => 'date' } ],
+ as => [qw/ min /],
+ }
+ );
+
+ return $rs->next()->get_column('min');
+}
+
+=head2 get_unique_holidays
+
+ my @unique_holidays = $calendar->get_unique_holidays();
+
+Returns an array of all the date objects that are unique holidays.
+
+=cut
+
+sub get_unique_holidays {
+ my $self = shift;
+ my $exclude_past = shift || 1;
+ my $branchcode = $self->{branchcode};
+ my @unique_holidays;
+ my $schema = Koha::Database->new->schema;
+
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ holiday_type => $HOLIDAYS->{EXCEPTION}
+ },
+ {
+ select => [{ DATE => 'date' }, 'note' ],
+ as => [qw/ date note/],
+ where => ($exclude_past ? \[' date >= CURRENT_DATE()'] : {} ),
+ }
+ );
+ while (my $date = $rs->next()){
+ my $outputdate = dt_from_string($date->date(), 'iso');
+ $outputdate = output_pref( { dt => $outputdate, dateonly => 1 } );
+ push @unique_holidays, {
+ date => $date->date(),
+ outputdate => $outputdate,
+ note => $date->note()
+ }
+ }
+
+ return @unique_holidays;
+}
+
+=head2 get_float_holidays
+
+ my @float_holidays = $calendar->get_float_holidays();
+
+Returns an array of all the date objects that are float holidays.
+
+=cut
+
+sub get_float_holidays {
+ my $self = shift;
+ my $exclude_past = shift || 1;
+ my $branchcode = $self->{branchcode};
+ my @float_holidays;
+ my $schema = Koha::Database->new->schema;
+
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ holiday_type => $HOLIDAYS->{FLOAT}
+ },
+ {
+ select => [{ DATE => 'date' }, 'note' ],
+ as => [qw/ date note/],
+ where => ($exclude_past ? \[' date >= CURRENT_DATE()'] : {} ),
+ }
+ );
+ while (my $date = $rs->next()){
+ my $outputdate = dt_from_string($date->date(), 'iso');
+ $outputdate = output_pref( { dt => $outputdate, dateonly => 1 } );
+ push @float_holidays, {
+ date => $date->date(),
+ outputdate => $outputdate,
+ note => $date->note()
+ }
+ }
+
+ return @float_holidays;
+}
+
+=head2 get_need_validation_holidays
+
+ my @need_validation_holidays = $calendar->get_need_validation_holidays();
+
+Returns an array of all the date objects that are float holidays in need of validation.
+
+=cut
+
+sub get_need_validation_holidays {
+ my $self = shift;
+ my $exclude_past = shift || 1;
+ my $branchcode = $self->{branchcode};
+ my @need_validation_holidays;
+ my $schema = Koha::Database->new->schema;
+
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ holiday_type => $HOLIDAYS->{NEED_VALIDATION}
+ },
+ {
+ select => [{ DATE => 'date' }, 'note' ],
+ as => [qw/ date note/],
+ where => ($exclude_past ? \[' date >= CURRENT_DATE()'] : {} ),
+ }
+ );
+ while (my $date = $rs->next()){
+ my $outputdate = dt_from_string($date->date(), 'iso');
+ $outputdate = output_pref( { dt => $outputdate, dateonly => 1 } );
+ push @need_validation_holidays, {
+ date => $date->date(),
+ outputdate => $outputdate,
+ note => $date->note()
+ }
+ }
+
+ return @need_validation_holidays;
+}
+
+=head2 get_repeatable_holidays
+
+ my @repeatable_holidays = $calendar->get_repeatable_holidays();
+
+Returns an array of all the date objects that are repeatable holidays.
+
+=cut
+
+sub get_repeatable_holidays {
+ my $self = shift;
+ my $exclude_past = shift || 1;
+ my $branchcode = $self->{branchcode};
+ my @repeatable_holidays;
+ my $schema = Koha::Database->new->schema;
+
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ holiday_type => $HOLIDAYS->{'REPEATABLE'},
+
+ },
+ {
+ select => \[ 'distinct DAY(date), MONTH(date), note'],
+ as => [qw/ day month note/],
+ where => ($exclude_past ? \[' date >= CURRENT_DATE()'] : {} ),
+ }
+ );
+
+ while (my $date = $rs->next()){
+ push @repeatable_holidays, {
+ day=> $date->get_column('day'),
+ month => $date->get_column('month'),
+ note => $date->note()
+ };
+ }
+
+ return @repeatable_holidays;
+}
+
+=head2 get_week_days_holidays
+
+ my @week_days_holidays = $calendar->get_week_days_holidays;
+
+Returns an array of all the date objects that are weekly holidays.
+
+=cut
+
+sub get_week_days_holidays {
+ my $self = shift;
+ my $exclude_past = shift || 1;
+ my $branchcode = $self->{branchcode};
+ my @week_days;
+ my $schema = Koha::Database->new->schema;
+
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ holiday_type => $HOLIDAYS->{WEEKLY},
+ branchcode => $branchcode,
+ },
+ {
+ select => [{ DAYOFWEEK => 'date'}, 'note'],
+ as => [qw/ weekday note /],
+ distinct => 1,
+ where => ($exclude_past ? \[' date >= CURRENT_DATE()'] : {} ),
+ }
+ );
+
+ while (my $date = $rs->next()){
+ push @week_days, {
+ weekday => ($date->get_column('weekday') -1),
+ note => $date->note()
+ };
+ }
+
+ return @week_days;
+}
+
+=head2 edit_holiday
+
+Modifies a date or a range of dates
+
+C<$title> Is the title to be modified for the holiday formed by $year/$month/$day.
+
+C<$weekday> Is the day of week for the holiday or the value everyday when it's for the whole range.
+
+C<$holiday_type> Is the type of the holiday :
+ E : Exception holiday, single day.
+ F : Floating holiday, different day each year.
+ N : Needs validation, copied float holiday from the past
+ R : Repeatable holiday, repeated on same date.
+ W : Weekly holiday, same day of the week.
+
+C<$open_hour> Is the opening hour.
+C<$close_hour> Is the closing hour.
+C<$start_date> Is the start of the range of dates.
+C<$end_date> Is the end of the range of dates.
+C<$delete_type> Delete all
+C<$today> Today based on the local date, using JavaScript.
+
+=cut
+
+sub edit_holiday {
+ my $self = shift;
+ my ($params) = @_;
+
+ my $title = $params->{title};
+ my $weekday = $params->{weekday} || '';
+ my $holiday_type = $params->{holiday_type};
+
+ my $start_date = $params->{start_date};
+ my $end_date = $params->{end_date};
+
+ my $open_hour = $params->{open_hour} || '';
+ my $close_hour = $params->{close_hour} || '';
+
+ my $delete_type = $params->{delete_type} || undef;
+ 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 = DateTime::Format::Strptime->new(pattern => "%F %T");
+
+ #String dates for Database usage
+ 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,
+ holiday_type => $holiday_type,
+ );
+ $updateValues{open_hour} = $open_hour if $open_hour ne '';
+ $updateValues{close_hour} = $close_hour if $close_hour ne '';
+
+ if($holiday_type eq $HOLIDAYS->{WEEKLY}) {
+ #Update weekly holidays
+ if($start_date_string eq $end_date_string ){
+ $end_date_string = $self->get_max_date();
+ }
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ },
+ {
+ where => \[ 'DAYOFWEEK(date) = ? AND date >= DATE(?) AND date <= DATE(?)', $weekday, $start_date_string, $end_date_string],
+ }
+ );
+
+ while (my $date = $rs->next()){
+ $date->update(\%updateValues);
+ }
+ }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]}};
+ 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]}]};
+ }
+ $where->{date}{'>='} = $today unless $override;
+
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ },
+ {
+ where => $where,
+ }
+ );
+ while (my $date = $rs->next()){
+ $date->update(\%updateValues);
+ }
+
+ }elsif ($holiday_type eq $HOLIDAYS->{REPEATABLE}) {
+ #Update repeatable holidays
+ my $parser = DateTime::Format::Strptime->new(
+ pattern => '%m-%d',
+ on_error => 'croak',
+ );
+ #Format the dates to have only month-day ex: 01-04 for January 4th
+ $start_date = $parser->format_datetime($start_date);
+ $end_date = $parser->format_datetime($end_date);
+ my $where = { -and => [ \["(DATE_FORMAT(date,'\%m-\%d') BETWEEN ? AND ?)", $start_date, $end_date] ] };
+ push @{$where->{'-and'}}, { 'date' => { '>=' => $today } } unless $override;
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ },
+ {
+ where => $where,
+ }
+ );
+ while (my $date = $rs->next()){
+ $date->update(\%updateValues);
+ }
+
+ }else {
+ #Update date(s)/Remove holidays
+ 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]}]};
+ }
+ $where->{date}{'>='} = $today unless $override;
+
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ },
+ {
+ where => $where,
+ }
+ );
+ #If none, the date(s) will be normal days, else,
+ if($holiday_type eq 'none'){
+ $updateValues{holiday_type} ='';
+ $updateValues{is_opened} =1;
+ }else{
+ delete $updateValues{holiday_type};
+ delete $updateValues{is_opened};
+ }
+
+ while (my $date = $rs->next()){
+ if($delete_type){
+ if($date->holiday_type() eq $HOLIDAYS->{WEEKLY}){
+ $self->remove_weekly_holidays($weekday, \%updateValues, $today);
+ }elsif($date->holiday_type() eq $HOLIDAYS->{REPEATABLE}){
+ $self->remove_repeatable_holidays($start_date, $end_date, \%updateValues, $today);
+ }
+ }else{
+ $date->update(\%updateValues);
+ }
+ }
+ }
+ $schema->storage->txn_commit;
+}
+
+=head2 remove_weekly_holidays
+
+ $calendar->remove_weekly_holidays($weekday, $updateValues, $today);
+
+Removes a weekly holiday and updates the days' parameters
+C<$weekday> is the weekday to un-holiday
+C<$updatevalues> is hashref containing the new parameters
+C<$today> is today's date
+
+=cut
+
+sub remove_weekly_holidays {
+ my ($self, $weekday, $updateValues, $today) = @_;
+ my $branchcode = $self->{branchcode};
+ my $schema = Koha::Database->new->schema;
+
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ is_opened => 0,
+ holiday_type => $HOLIDAYS->{WEEKLY}
+ },
+ {
+ where => {-and => [ \["DAYOFWEEK(date) = ?", $weekday], date => { '>=' => $today}]},
+ }
+ );
+
+ while (my $date = $rs->next()){
+ $date->update($updateValues);
+ }
+}
+
+=head2 remove_repeatable_holidays
+
+ $calendar->remove_repeatable_holidays($startDate, $endDate, $today);
+
+Removes a repeatable holiday and updates the days' parameters
+C<$startDatey> is the start date of the repeatable holiday
+C<$endDate> is the end date of the repeatble holiday
+C<$updatevalues> is hashref containing the new parameters
+C<$today> is today's date
+
+=cut
+
+sub remove_repeatable_holidays {
+ my ($self, $startDate, $endDate, $updateValues, $today) = @_;
+ my $branchcode = $self->{branchcode};
+ my $schema = Koha::Database->new->schema;
+ my $parser = DateTime::Format::Strptime->new(
+ pattern => '%m-%d',
+ on_error => 'croak',
+ );
+ #Format the dates to have only month-day ex: 01-04 for January 4th
+ $startDate = $parser->format_datetime($startDate);
+ $endDate = $parser->format_datetime($endDate);
+
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ is_opened => 0,
+ holiday_type => $HOLIDAYS->{REPEATABLE},
+ },
+ {
+ where => { -and => [date => { '>=' => $today}, \["(DATE_FORMAT(date,'\%m-\%d') BETWEEN ? AND ?)", $startDate, $endDate]]},
+ }
+ );
+
+ while (my $date = $rs->next()){
+ $date->update($updateValues);
+ }
+}
+
+=head2 copy_to_branch
+
+ $calendar->copy_to_branch($branch2);
+
+Copies the days and holidays from this branch to $branch2, ignoring dates in C<$self>
+but not in C<$branch2>
+
+C<$branch2> the branch to copy into
+
+=cut
+
+sub copy_to_branch {
+ my ($self,$newBranch) =@_;
+ my $branchcode = $self->{branchcode};
+ my $schema = Koha::Database->new->schema;
+
+ my $copyFrom = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode
+ },
+ {
+ columns => [ qw/ date is_opened note holiday_type open_hour close_hour /]
+ }
+ );
+ while (my $copyDate = $copyFrom->next()){
+ my $copyTo = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $newBranch,
+ date => $copyDate->date(),
+ },
+ {
+ columns => [ qw/ date branchcode is_opened note holiday_type open_hour close_hour /]
+ }
+ );
+ #if the date does not exist in the copyTO branch, than skip it.
+ if($copyTo->count ==0){
+ next;
+ }
+ $copyTo->next()->update({
+ is_opened => $copyDate->is_opened(),
+ holiday_type => $copyDate->holiday_type(),
+ note => $copyDate->note(),
+ open_hour => $copyDate->open_hour(),
+ close_hour => $copyDate->close_hour()
+ });
+ }
+}
+
+=head2 is_opened
+
+ $calendar->is_opened($date)
+
+Returns whether the library is open on C<$date>
+
+=cut
+
+sub is_opened {
+ my($self, $date) = @_;
+ my $branchcode = $self->{branchcode};
+ my $schema = Koha::Database->new->schema;
+ my $dtf = $schema->storage->datetime_parser;
+ $date= $dtf->format_datetime($date);
+ #if the date is not found
+ my $is_opened = -1;
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ },
+ {
+ where => \['date = DATE(?)', $date]
+ }
+ );
+ $is_opened = $rs->next()->is_opened() if $rs->count() != 0;
+
+ return $is_opened;
+}
+
+=head2 is_holiday
+
+ $calendar->is_holiday($date)
+
+Returns whether C<$date> is a holiday or not
+
+=cut
+
+sub is_holiday {
+ my($self, $date) = @_;
+ my $branchcode = $self->{branchcode};
+ my $schema = Koha::Database->new->schema;
+ my $dtf = $schema->storage->datetime_parser;
+ $date= $dtf->format_datetime($date);
+ #if the date is not found
+ my $isHoliday = -1;
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ },
+ {
+ where => \['date = DATE(?)', $date]
+ }
+ );
+
+ if ($rs->count() != 0) {
+ $isHoliday = ($rs->first()->is_opened() ? 0 : 1);
+ }
+
+ return $isHoliday;
+}
+
+=head2 copy_holiday
+
+ $calendar->copy_holiday($from_startDate, $from_endDate, $to_startDate, $to_endDate, $daysnumber);
+
+Copies a holiday's parameters from a range to a new range
+C<$from_startDate> the source holiday's start date
+C<$from_endDate> the source holiday's end date
+C<$to_startDate> the destination holiday's start date
+C<$to_endDate> the destination holiday's end date
+C<$daysnumber> the number of days in the range.
+
+Both ranges should have the same number of days in them.
+
+=cut
+
+sub copy_holiday {
+ my ($self, $from_startDate, $from_endDate, $to_startDate, $to_endDate, $daysnumber) = @_;
+ my $branchcode = $self->{branchcode};
+ my $copyFromType = $from_startDate && $from_endDate eq '' ? 'oneDay': 'range';
+ my $schema = Koha::Database->new->schema;
+ my $dtf = $schema->storage->datetime_parser;
+
+ if ($copyFromType eq 'oneDay'){
+ my $where;
+ $to_startDate = $dtf->format_datetime($to_startDate);
+ if ($to_startDate && $to_endDate) {
+ $to_endDate = $dtf->format_datetime($to_endDate);
+ $where = { date => { -between => [$to_startDate, $to_endDate]}};
+ } else {
+ $where = { date => $to_startDate };
+ }
+
+ $from_startDate = $dtf->format_datetime($from_startDate);
+ my $fromDate = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ date => $from_startDate
+ }
+ );
+ my $toDates = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ },
+ {
+ where => $where
+ }
+ );
+
+ my $copyDate = $fromDate->next();
+ while (my $date = $toDates->next()){
+ $date->update({
+ is_opened => $copyDate->is_opened(),
+ holiday_type => $copyDate->holiday_type(),
+ note => $copyDate->note(),
+ open_hour => $copyDate->open_hour(),
+ close_hour => $copyDate->close_hour()
+ })
+ }
+
+ }else{
+ my $endDate = dt_from_string($from_endDate);
+ $to_startDate = $dtf->format_datetime($to_startDate);
+ $to_endDate = $dtf->format_datetime($to_endDate);
+ if($daysnumber == 7){
+ for (my $tempDate = $from_startDate->clone(); $tempDate <= $endDate;$tempDate->add(days => 1)){
+ my $formatedDate = $dtf->format_datetime($tempDate);
+ my $fromDate = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ date => $formatedDate,
+ },
+ {
+ select => [{ DAYOFWEEK => 'date' }],
+ as => [qw/ weekday /],
+ columns =>[ qw/ holiday_type note open_hour close_hour note/]
+ }
+ );
+ my $copyDate = $fromDate->next();
+ my $weekday = $copyDate->get_column('weekday');
+
+ my $toDate = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+
+ },
+ {
+ where => {date => {-between => [$to_startDate, $to_endDate]}, "DAYOFWEEK(date)" => $weekday},
+ }
+ );
+ my $copyToDate = $toDate->next();
+ $copyToDate->update({
+ is_opened => $copyDate->is_opened(),
+ holiday_type => $copyDate->holiday_type(),
+ note => $copyDate->note(),
+ open_hour => $copyDate->open_hour(),
+ close_hour => $copyDate->close_hour()
+ });
+
+ }
+ }else{
+ my $to_startDate = dt_from_string($to_startDate);
+ my $to_endDate = dt_from_string($to_endDate);
+ for (my $tempDate = $from_startDate->clone(); $tempDate <= $endDate;$tempDate->add(days => 1)){
+ my $from_formatedDate = $dtf->format_datetime($tempDate);
+ my $fromDate = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ date => $from_formatedDate,
+ },
+ {
+ order_by => { -asc => 'date' }
+ }
+ );
+ my $to_formatedDate = $dtf->format_datetime($to_startDate);
+ my $toDate = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ date => $to_formatedDate
+ },
+ {
+ order_by => { -asc => 'date' }
+ }
+ );
+ my $copyDate = $fromDate->next();
+ $toDate->next()->update({
+ is_opened => $copyDate->is_opened(),
+ holiday_type => $copyDate->holiday_type(),
+ note => $copyDate->note(),
+ open_hour => $copyDate->open_hour(),
+ close_hour => $copyDate->close_hour()
+ });
+ $to_startDate->add(days =>1);
+ }
+ }
+
+
+ }
+}
+
+=head2 days_between
+
+ $cal->days_between( $start_date, $end_date )
+
+Calculates the number of days the library is opened between C<$start_date> and C<$end_date>
+
+=cut
+
+sub days_between {
+ my ($self, $start_date, $end_date, ) = @_;
+ my $branchcode = $self->{branchcode};
+
+ if ( $start_date->compare($end_date) > 0 ) {
+ # swap dates
+ ($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->clone->truncate(to => 'day'));
+ $end_date = $dtf->format_datetime($end_date->clone->truncate(to => 'day'));
+
+ my $days_between = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ is_opened => 1,
+ },
+ {
+ where => \['date >= date(?) AND date < date(?)',$start_date, $end_date]
+ }
+ );
+
+ return DateTime::Duration->new( days => $days_between->count());
+}
+
+=head2 next_open_day
+
+ $open_date = $self->next_open_day($base_date);
+
+Returns a string representing the next day the library is open, starting from C<$base_date>
+
+=cut
+
+sub next_open_day {
+ my ( $self, $date ) = @_;
+ my $branchcode = $self->{branchcode};
+ my $schema = Koha::Database->new->schema;
+ my $dtf = $schema->storage->datetime_parser;
+ $date = $dtf->format_datetime($date);
+
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ is_opened => 1,
+ },
+ {
+ where => \['date > date(?)', $date],
+ order_by => { -asc => 'date' },
+ rows => 1
+ }
+ );
+ return dt_from_string( $rs->next()->date(), 'iso');
+}
+
+=head2 prev_open_day
+
+ $open_date = $self->prev_open_day($base_date);
+
+Returns a string representing the closest previous day the library was open, starting from C<$base_date>
+
+=cut
+
+sub prev_open_day {
+ my ( $self, $date ) = @_;
+ my $branchcode = $self->{branchcode};
+ my $schema = Koha::Database->new->schema;
+ my $dtf = $schema->storage->datetime_parser;
+ $date = $dtf->format_datetime($date);
+
+ my $rs = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ is_opened => 1,
+ },
+ {
+ where => \['date < date(?)', $date],
+ order_by => { -desc => 'date' },
+ rows => 1
+ }
+ );
+ return dt_from_string( $rs->next()->date(), 'iso');
+}
+
+=head2 days_forward
+
+ $fwrd_date = $calendar->days_forward($start, $count)
+
+Returns the date C<$count> days in the future from C<$start>, ignoring days where the library is closed.
+
+=cut
+
+sub days_forward {
+ my $self = shift;
+ my $start_dt = shift;
+ my $num_days = shift;
+
+ return $start_dt unless $num_days > 0;
+
+ my $base_dt = $start_dt->clone();
+
+ while ($num_days--) {
+ $base_dt = $self->next_open_day($base_dt);
+ }
+
+ return $base_dt;
+}
+
+=head2 hours_between
+
+ $hours = $calendar->hours_between($start_dt, $end_dt)
+
+Returns the number of hours between C<$start_dt> and C<$end_dt>. This is the imprecise
+version, which simply calculates the number of day times 24. To take opening hours into account
+see C<open_hours_between>/
+
+=cut
+
+sub hours_between {
+ my ($self, $start_dt, $end_dt) = @_;
+ my $branchcode = $self->{branchcode};
+ my $schema = Koha::Database->new->schema;
+ my $dtf = $schema->storage->datetime_parser;
+ my $start_date = $start_dt->clone();
+ my $end_date = $end_dt->clone();
+ my $duration = $end_date->delta_ms($start_date);
+ $start_date->truncate( to => 'day' );
+ $end_date->truncate( to => 'day' );
+
+ # NB this is a kludge in that it assumes all days are 24 hours
+ # However for hourly loans the logic should be expanded to
+ # take into account open/close times then it would be a duration
+ # of library open hours
+ my $skipped_days = 0;
+ $start_date = $dtf->format_datetime($start_date);
+ $end_date = $dtf->format_datetime($end_date);
+ my $hours_between = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ is_opened => 0
+ },
+ {
+ where => {date => {-between => [$start_date, $end_date]}},
+ }
+ );
+
+ if ($skipped_days = $hours_between->count()) {
+ $duration->subtract_duration(DateTime::Duration->new( hours => 24 * $skipped_days));
+ }
+
+ return $duration;
+}
+
+=head2 open_hours_between
+
+ $hours = $calendar->open_hours_between($start_date, $end_date)
+
+Returns the number of hours between C<$start_date> and C<$end_date>, taking into
+account the opening hours of the library.
+
+=cut
+
+sub open_hours_between {
+ my ($self, $start_date, $end_date) = @_;
+ my $branchcode = $self->{branchcode};
+ my $schema = Koha::Database->new->schema;
+ my $dtf = DateTime::Format::Strptime->new(pattern => "%F %T");
+ $start_date = $dtf->format_datetime($start_date);
+ $end_date = $dtf->format_datetime($end_date);
+
+ my $working_hours_between = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ is_opened => 1,
+ },
+ {
+ select => \['sum(time_to_sec(timediff(close_hour, open_hour)) / 3600)'],
+ as => [qw /hours_between/],
+ where => \['date BETWEEN DATE(?) AND DATE(?)', $start_date, $end_date]
+ }
+ );
+
+ my $loan_day = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ },
+ {
+ order_by => \[ 'ABS(DATEDIFF(date, ?))', $start_date ],
+ rows => 1,
+ }
+ );
+
+ my $return_day = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ },
+ {
+ order_by => \[ 'ABS(DATEDIFF(date, ?))', $end_date ],
+ rows => 1,
+ }
+ );
+
+ #Capture the time portion of the date
+ $start_date =~ /\s(.*)/;
+ my $loan_date_time = $1;
+ $end_date =~ /\s(.*)/;
+ my $return_date_time = $1;
+
+ my $not_used_hours = $schema->resultset('DiscreteCalendar')->search(
+ {
+ branchcode => $branchcode,
+ is_opened => 1,
+ },
+ {
+ select => \[ '(time_to_sec(timediff(?, ?)) + time_to_sec(timediff(?, ?)) ) / 3600', $return_day->next()->close_hour(), $return_date_time, $loan_date_time, $loan_day->next()->open_hour()],
+ as => [qw /not_used_hours/],
+ }
+ );
+
+ return ($working_hours_between->next()->get_column('hours_between') - $not_used_hours->next()->get_column('not_used_hours'));
+}
+
+=head2 addDate
+
+ my $dt = $calendar->addDate($date, $dur, $unit)
+
+C<$date> is a DateTime object representing the starting date of the interval.
+C<$offset> is a duration to add to it (DateTime::Duration objects are supported as legacy)
+C<$unit> is a string value 'days' or 'hours' toflag granularity of duration
+
+=cut
+
+sub addDate {
+ my ( $self, $startdate, $add_duration, $unit ) = @_;
+
+ # Default to days duration (legacy support I guess)
+ if ( ref $add_duration ne 'DateTime::Duration' ) {
+ $add_duration = DateTime::Duration->new( days => $add_duration );
+ }
+
+ $unit ||= 'days'; # default days ?
+ my $dt;
+
+ if ( $unit eq 'hours' ) {
+ # Fixed for legacy support. Should be set as a branch parameter
+ my $return_by_hour = 10;
+
+ $dt = $self->addHours($startdate, $add_duration, $return_by_hour);
+ } else {
+ # days
+ $dt = $self->addDays($startdate, $add_duration);
+ }
+
+ return $dt;
+}
+
+=head2 addHours
+
+ $end = $calendar->addHours($start, $hours_duration, $return_by_hour)
+
+Add C<$hours_duration> to C<$start> date.
+C<$return_by_hour> is an integer value representing the opening hour for the branch
+
+=cut
+
+sub addHours {
+ my ( $self, $startdate, $hours_duration, $return_by_hour ) = @_;
+ my $base_date = $startdate->clone();
+
+ $base_date->add_duration($hours_duration);
+
+ # If we are using the calendar behave for now as if Datedue
+ # was the chosen option (current intended behaviour)
+
+ if ( $self->{days_mode} ne 'Days' &&
+ $self->is_holiday($base_date) ) {
+
+ if ( $hours_duration->is_negative() ) {
+ $base_date = $self->prev_open_day($base_date);
+ } else {
+ $base_date = $self->next_open_day($base_date);
+ }
+
+ $base_date->set_hour($return_by_hour);
+
+ }
+
+ return $base_date;
+}
+
+=head2 addDays
+
+ $date = $calendar->addDays($start, $duration)
+
+Add C<$days_duration> to C<$start> date. If the calendar's days_mode is set
+to 'Calendar', it ignores closed days. Else if the calendar is set to 'Datedue'
+it calculates the date normally, and then pushes to result to the next open day.
+
+=cut
+
+sub addDays {
+ my ( $self, $startdate, $days_duration ) = @_;
+ my $base_date = $startdate->clone();
+
+ $self->{days_mode} ||= q{};
+
+ if ( $self->{days_mode} eq 'Calendar' ) {
+ # use the calendar to skip all days the library is closed
+ # when adding
+ my $days = abs $days_duration->in_units('days');
+
+ if ( $days_duration->is_negative() ) {
+ while ($days) {
+ $base_date = $self->prev_open_day($base_date);
+ --$days;
+ }
+ } else {
+ while ($days) {
+ $base_date = $self->next_open_day($base_date);
+ --$days;
+ }
+ }
+
+ } else { # Days or Datedue
+ # use straight days, then use calendar to push
+ # the date to the next open day if Datedue
+ $base_date->add_duration($days_duration);
+
+ if ( $self->{days_mode} eq 'Datedue' ) {
+ # Datedue, then use the calendar to push
+ # the date to the next open day if holiday
+ if (!$self->is_opened($base_date) ) {
+
+ if ( $days_duration->is_negative() ) {
+ $base_date = $self->prev_open_day($base_date);
+ } else {
+ $base_date = $self->next_open_day($base_date);
+ }
+ }
+ }
+ }
+
+ return $base_date;
+}
+
+1;
diff --git a/Koha/Hold.pm b/Koha/Hold.pm
index 360b43e7b5..eaaab1aae8 100644
--- a/Koha/Hold.pm
+++ b/Koha/Hold.pm
@@ -34,7 +34,7 @@ use Koha::Biblios;
use Koha::Items;
use Koha::Libraries;
use Koha::Old::Holds;
-use Koha::Calendar;
+use Koha::DiscreteCalendar;
use Koha::Exceptions::Hold;
@@ -66,7 +66,7 @@ sub age {
my $age;
if ( $use_calendar ) {
- my $calendar = Koha::Calendar->new( branchcode => $self->branchcode );
+ my $calendar = Koha::DiscreteCalendar->new({ branchcode => $self->branchcode });
$age = $calendar->days_between( dt_from_string( $self->reservedate ), $today );
}
else {
@@ -193,7 +193,7 @@ sub set_waiting {
branchcode => $self->branchcode,
}
);
- my $calendar = Koha::Calendar->new( branchcode => $self->branchcode, days_mode => $daysmode );
+ my $calendar = Koha::DiscreteCalendar->new( branchcode => $self->branchcode, days_mode => $daysmode );
$expirationdate = $calendar->days_forward( dt_from_string(), $max_pickup_delay );
}
diff --git a/Koha/Schema/Result/DiscreteCalendar.pm b/Koha/Schema/Result/DiscreteCalendar.pm
new file mode 100644
index 0000000000..6db32c892e
--- /dev/null
+++ b/Koha/Schema/Result/DiscreteCalendar.pm
@@ -0,0 +1,111 @@
+use utf8;
+package Koha::Schema::Result::DiscreteCalendar;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+=head1 NAME
+
+Koha::Schema::Result::DiscreteCalendar
+
+=cut
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+=head1 TABLE: C<discrete_calendar>
+
+=cut
+
+__PACKAGE__->table("discrete_calendar");
+
+=head1 ACCESSORS
+
+=head2 date
+
+ data_type: 'datetime'
+ datetime_undef_if_invalid: 1
+ is_nullable: 0
+
+=head2 branchcode
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 10
+
+=head2 is_opened
+
+ data_type: 'tinyint'
+ default_value: 1
+ is_nullable: 1
+
+=head2 holiday_type
+
+ data_type: 'varchar'
+ default_value: (empty string)
+ is_nullable: 1
+ size: 1
+
+=head2 note
+
+ data_type: 'varchar'
+ default_value: (empty string)
+ is_nullable: 1
+ size: 30
+
+=head2 open_hour
+
+ data_type: 'time'
+ is_nullable: 0
+
+=head2 close_hour
+
+ data_type: 'time'
+ is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+ "date",
+ {
+ data_type => "datetime",
+ datetime_undef_if_invalid => 1,
+ is_nullable => 0,
+ },
+ "branchcode",
+ { data_type => "varchar", is_nullable => 0, size => 10 },
+ "is_opened",
+ { data_type => "tinyint", default_value => 1, is_nullable => 1 },
+ "holiday_type",
+ { data_type => "varchar", default_value => "", is_nullable => 1, size => 1 },
+ "note",
+ { data_type => "varchar", default_value => "", is_nullable => 1, size => 30 },
+ "open_hour",
+ { data_type => "time", is_nullable => 0 },
+ "close_hour",
+ { data_type => "time", is_nullable => 0 },
+);
+
+=head1 PRIMARY KEY
+
+=over 4
+
+=item * L</branchcode>
+
+=item * L</date>
+
+=back
+
+=cut
+
+__PACKAGE__->set_primary_key("branchcode", "date");
+
+
+# Created by DBIx::Class::Schema::Loader v0.07045 @ 2017-04-19 10:07:41
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:wtctW8ZzCkyCZFZmmavFEw
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+1;
diff --git a/circ/returns.pl b/circ/returns.pl
index a7556c7b07..eb9e3ea03a 100755
--- a/circ/returns.pl
+++ b/circ/returns.pl
@@ -50,9 +50,9 @@ use C4::Reserves;
use C4::RotatingCollections;
use Koha::AuthorisedValues;
use Koha::BiblioFrameworks;
-use Koha::Calendar;
use Koha::Checkouts;
use Koha::DateUtils;
+use Koha::DiscreteCalendar;
use Koha::Holds;
use Koha::Items;
use Koha::Patrons;
@@ -194,6 +194,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 });
#dropbox: get last open day (today - 1)
my $dropboxdate = Koha::Checkouts::calculate_dropbox_date();
diff --git a/koha-tmpl/intranet-tmpl/prog/css/discretecalendar.css b/koha-tmpl/intranet-tmpl/prog/css/discretecalendar.css
new file mode 100644
index 0000000000..3b7625e1dd
--- /dev/null
+++ b/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;
+}
\ No newline at end of file
diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc
index ba3f54a281..97e43fbbcd 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc
+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc
@@ -107,7 +107,7 @@
<h5>Additional tools</h5>
<ul>
[% IF ( CAN_user_tools_edit_calendar ) %]
- <li><a href="/cgi-bin/koha/tools/holidays.pl">Calendar</a></li>
+ <li><a href="/cgi-bin/koha/tools/discrete_calendar.pl">Calendar</a></li>
[% END %]
[% IF ( CAN_user_tools_manage_csv_profiles ) %]
<li><a href="/cgi-bin/koha/tools/csv-profiles.pl">CSV profiles</a></li>
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/discrete_calendar.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/discrete_calendar.tt
new file mode 100644
index 0000000000..4c15aec519
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/discrete_calendar.tt
@@ -0,0 +1,678 @@
+[% USE Asset %]
+[% USE Branches %]
+[% SET footerjs = 1 %]
+[% INCLUDE 'doc-head-open.inc' %]
+<title>Koha › Tools › [% Branches.GetName( branch ) %] calendar</title>
+[% INCLUDE 'doc-head-close.inc' %]
+[% Asset.css("css/datatables.css") %]
+[% Asset.css("css/discretecalendar.css") %]
+</head>
+
+<body id="tools_holidays" class="tools">
+[% INCLUDE 'header.inc' %]
+[% INCLUDE 'cat-search.inc' %]
+
+<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> › <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a> › [% Branches.GetName( branch ) %] calendar</div>
+
+<div id="doc3" class="yui-t1">
+
+ <div id="bd">
+ <div id="yui-main">
+ <div class="yui-b">
+ <h2>[% Branches.GetName( branch ) %] calendar</h2>
+ <div class="yui-g">
+ <div class="yui-u first" style="width:60%">
+ <label for="branch">Define the holidays for:</label>
+ <form method="post" onsubmit="return validateForm('CopyCalendar')">
+ <select id="branch" name="branch">
+ [% PROCESS options_for_libraries libraries => Branches.all( selected => branch ) %]
+ </select>
+ Copy calendar to
+ <select id='newBranch' name ='newBranch'>
+ <option value=""></option>
+ [% FOREACH l IN Branches.all() %]
+ [% UNLESS branch == l.branchcode %]
+ <option value="[% l.branchcode %]">[% l.branchname %]</option>
+ [% END %]
+ [% END %]
+ </select>
+ <input type="hidden" name="action" value="copyBranch" />
+ <input type="submit" value="Clone">
+ </form>
+ <h3>Calendar information</h3>
+ <div id="jcalendar-container" style="float: left"></div>
+ <!-- ***************************** Panel to deal with new holidays ********************** -->
+ [% UNLESS datesInfos %]
+ <div class="alert alert-danger" style="float: left; margin-left:15px">
+ <strong>Error!</strong> You have to run generate_discrete_calendar.pl in order to use Discrete Calendar.
+ </div>
+ [% END %]
+
+ [% IF no_branch_selected %]
+ <div class="alert alert-danger" style="float: left; margin-left:15px">
+ <strong>No library set!</strong> You are using the default calendar.
+ </div>
+ [% END %]
+
+ <div class="panel newHoliday" id="newHoliday" style="float: left; margin-left:15px">
+ <form method="post" onsubmit="return validateForm('newHoliday')">
+ <fieldset class="brief">
+ <h3>Edit date details</h3>
+ <span id="holtype"></span>
+ <ol>
+ <li>
+ <strong>Library:</strong>
+ <span id="newBranchNameOutput"></span>
+ <input type="hidden" id="branch" name="branch" />
+ </li>
+ <li>
+ <strong>From date:</strong>
+ <span id="newDaynameOutput"></span>,
+
+ [% IF ( dateformat == "us" ) %]<span id="newMonthOutput"></span>/<span id="newDayOutput"></span>/<span id="newYearOutput"></span>[% ELSIF ( dateformat == "metric" ) %]<span id="newDayOutput"></span>/<span id="newMonthOutput"></span>/<span id="newYearOutput"></span>[% ELSIF ( dateformat == "dmydot" ) %]<span id="newDayOutput"></span>.<span id="newMonthOutput"></span>.<span id="newYearOutput"></span>[% ELSE %]<span id="newYearOutput"></span>/<span id="newMonthOutput"></span>/<span id="newDayOutput"></span>[% END %]
+
+ <input type="hidden" id="newDayname" name="showDayname" />
+ <input type="hidden" id="Day" name="Day" />
+ <input type="hidden" id="Month" name="Month" />
+ <input type="hidden" id="Year" name="Year" />
+ </li>
+ <li class="dateinsert">
+ <b>To date: </b>
+ <input type="text" id="from_copyToDatePicker" name="toDate" size="20" class="datepicker" />
+ </li>
+ <li>
+ <label for="title">Title: </label><input type="text" name="Title" id="title" size="35" />
+ </li>
+ <li id="holidayType">
+ <label for="holidayType">Date type</label>
+ <select name ='holidayType'>
+ <option value="empty"></option>
+ <option value="none">Working day</option>
+ <option value="E">Unique holiday</option>
+ <option value="W">Weekly holiday</option>
+ <option value="R">Repeatable holiday</option>
+ <option value="F">Floating holiday</option>
+ <option value="N" disabled>Need validation</option>
+ </select>
+ </li>
+ <li id="days_of_week" style="display :none">
+ <label for="day_of_week">Week day</label>
+ <select name ='day_of_week'>
+ <option value="everyday">Everyday</option>
+ <option value="1">Sundays</option>
+ <option value="2">Mondays</option>
+ <option value="3">Tuesdays</option>
+ <option value="4">Wednesdays</option>
+ <option value="5">Thursdays</option>
+ <option value="6">Fridays</option>
+ <option value="7">Saturdays</option>
+ </select>
+ </li>
+ <li class="radio" id="deleteType" style="display : none;" >
+ <input type="checkbox" name="deleteType" id="deleteType_checkbox" value="1" ><label for="deleteType_checkbox"> Delete this type</label>
+ <a href="#" class="helptext">[?]</a>
+ <div class="hint">Remove all repeated or weekly holidays of the selected date or week day <br> if working day is selected.</div>
+ </li>
+ <li>
+ <label for="openHour">Open hours: </label><input type="text" name="openHour" id='openHour' style="display :flex" >
+ </li>
+ <li>
+ <label for="closeHour">Close hours: </label><input type="text" name="closeHour" id='closeHour' style="display :flex" >
+ </li>
+ <li class="radio">
+ <input type="radio" name="action" id="EditRadioButton" value="edit" checked/>
+ <label for="EditRadioButton">Edit selected dates</label>
+ </li>
+ <li class="radio">
+ <input type="radio" name="action" id="CopyRadioButton" value="copyDates" />
+ <label for="CopyRadioButton">Copy to different dates</label>
+ <div class="CopyDatePanel" style="display:none; padding-left:15px">
+ <b>From : </b>
+ <input type="text" id="to_copyFromDatePicker" size="20" class="datepicker"/>
+ <b>To : </b>
+ <input type="text" id="to_copyToDatePicker" size="20" class="datepicker"/>
+ </div>
+ <input type="hidden" name="daysnumber" id='daysnumber'>
+ <!-- These yyyy-mm-dd -->
+ <input type="hidden" name="from_copyFrom" id='from_copyFrom'>
+ <input type="hidden" name="from_copyTo" id='from_copyTo'>
+ <input type="hidden" name="to_copyFrom" id='to_copyFrom'>
+ <input type="hidden" name="to_copyTo" id='to_copyTo'>
+ <input type="hidden" name="local_today" id='local_today'>
+ </li>
+ </ol>
+ <fieldset class="action">
+ <input type="submit" name="submit" value="Save" />
+ <a href="#" class="cancel hidePanel newHoliday">Cancel</a>
+ </fieldset>
+ </fieldset>
+ </form>
+ </div>
+
+<!-- ************************************************************************************** -->
+<!-- ****** MAIN SCREEN CODE ****** -->
+<!-- ************************************************************************************** -->
+
+</div>
+<div class="yui-u" style="width : 40%">
+ <div class="help">
+ <h4>Hints</h4>
+ <ul>
+ <li>Search in the calendar the day you want to set as holiday.</li>
+ <li>Click the date to add or edit a holiday.</li>
+ <li>Specify how the holiday should repeat.</li>
+ <li>Click Save to finish.</li>
+ <li>PS:
+ <ul>
+ <li>Past dates cannot be changed</li>
+ <li>Weekly holidays change open/close hours for all the days affected unless inputs are empty</li>
+ </ul>
+ </li>
+ </ul>
+ <h4>Key</h4>
+ <p>
+ <span class="key normalday">Working day </span>
+ <span class="key holiday">Unique holiday</span>
+ <span class="key repeatableweekly">Holiday repeating weekly</span>
+ <span class="key repeatableyearly">Holiday repeating yearly</span>
+ <span class="key float">Floating holiday</span>
+ <span class="key exception">Need validation</span>
+ </p>
+ </div>
+<div id="holiday-list">
+
+ [% IF ( NEED_VALIDATION_HOLIDAYS ) %]
+ <h3>Need validation holidays</h3>
+ <table id="holidaysunique">
+ <thead>
+ <tr>
+ <th class="exception">Date</th>
+ <th class="exception">Title</th>
+ </tr>
+ </thead>
+ <tbody>
+ [% FOREACH need_validation_holiday IN NEED_VALIDATION_HOLIDAYS %]
+ <tr>
+ <td><a href="#doc3" onclick="go_to_date('[% need_validation_holiday.date %]')"><span title="[% need_validation_holiday.DATE_SORT %]">[% need_validation_holiday.outputdate %]</span></a></td>
+ <td>[% need_validation_holiday.note %]</td>
+ </tr>
+ [% END %]
+ </tbody>
+ </table>
+ [% END %]
+
+ [% IF ( WEEKLY_HOLIDAYS ) %]
+ <h3>Weekly - Repeatable holidays</h3>
+ <table id="holidayweeklyrepeatable">
+ <thead>
+ <tr>
+ <th class="repeatableweekly">Day of week</th>
+ <th class="repeatableweekly">Title</th>
+ </tr>
+ </thead>
+ <tbody>
+ [% FOREACH WEEK_DAYS_LOO IN WEEKLY_HOLIDAYS %]
+ <tr>
+ <td>[% WEEK_DAYS_LOO.weekday %]</td>
+ </td>
+ <td>[% WEEK_DAYS_LOO.note %]</td>
+ </tr>
+ [% END %]
+ </tbody>
+</table>
+[% END %]
+
+[% IF ( REPEATABLE_HOLIDAYS ) %]
+<h3>Yearly - Repeatable holidays</h3>
+<table id="holidaysyearlyrepeatable">
+ <thead>
+ <tr>
+ [% IF ( dateformat == "metric" ) %]
+ <th class="repeatableyearly">Day/month</th>
+ [% ELSE %]
+ <th class="repeatableyearly">Month/day</th>
+ [% END %]
+ <th class="repeatableyearly">Title</th>
+ </tr>
+ </thead>
+ <tbody>
+ [% FOREACH DAY_MONTH_HOLIDAYS_LOO IN REPEATABLE_HOLIDAYS %]
+ <tr>
+ [% IF ( dateformat == "metric" ) %]
+ <td><span title="[% DAY_MONTH_HOLIDAYS_LOO.DATE_SORT %]">[% DAY_MONTH_HOLIDAYS_LOO.day %]/[% DAY_MONTH_HOLIDAYS_LOO.month %]</span></td>
+ [% ELSE %]
+ <td><span title="[% DAY_MONTH_HOLIDAYS_LOO.DATE_SORT %]">[% DAY_MONTH_HOLIDAYS_LOO.month %]/[% DAY_MONTH_HOLIDAYS_LOO.day %]</span></td>
+ [% END %]
+ <td>[% DAY_MONTH_HOLIDAYS_LOO.note %]</td>
+ </tr>
+ [% END %]
+ </tbody>
+</table>
+[% END %]
+
+[% IF ( UNIQUE_HOLIDAYS ) %]
+<h3>Unique holidays</h3>
+<table id="holidaysunique">
+ <thead>
+ <tr>
+ <th class="holiday">Date</th>
+ <th class="holiday">Title</th>
+ </tr>
+ </thead>
+ <tbody>
+ [% FOREACH HOLIDAYS_LOO IN UNIQUE_HOLIDAYS %]
+ <tr>
+ <td><a href="#doc3" onclick="go_to_date('[% HOLIDAYS_LOO.date %]')"><span title="[% HOLIDAYS_LOO.DATE_SORT %]">[% HOLIDAYS_LOO.outputdate %]</span></a></td>
+ <td>[% HOLIDAYS_LOO.note %]</td>
+ </tr>
+ [% END %]
+ </tbody>
+</table>
+[% END %]
+
+[% IF ( FLOAT_HOLIDAYS ) %]
+<h3>Floating holidays</h3>
+<table id="holidaysunique">
+ <thead>
+ <tr>
+ <th class="float">Date</th>
+ <th class="float">Title</th>
+ </tr>
+ </thead>
+ <tbody>
+ [% FOREACH float_holiday IN FLOAT_HOLIDAYS %]
+ <tr>
+ <td><a href="#doc3" onclick="go_to_date('[% float_holiday.date %]')"><span title="[% float_holiday.DATE_SORT %]">[% float_holiday.outputdate %]</span></a></td>
+ <td>[% float_holiday.note %]</td>
+ </tr>
+ [% END %]
+ </tbody>
+</table>
+[% END %]
+</div>
+</div>
+</div>
+</div>
+</div>
+
+<div class="yui-b noprint">
+[% INCLUDE 'tools-menu.inc' %]
+</div>
+</div>
+[% MACRO jsinclude BLOCK %]
+[% Asset.js("lib/jquery/plugins/jquery-ui-timepicker-addon.min.js") %]
+[% INCLUDE 'calendar.inc' %]
+[% INCLUDE 'datatables.inc' %]
+<script type="text/javascript">
+ //<![CDATA[
+ var weekdays = new Array(_("Sundays"),_("Mondays"),_("Tuesdays"),_("Wednesdays"),_("Thursdays"),_("Fridays"),_("Saturdays"));
+ // Array containing all the information about each date in the calendar.
+ var datesInfos = new Array();
+ [% FOREACH date IN datesInfos %]
+ datesInfos["[% date.date %]"] = {
+ title : "[% date.note %]",
+ outputdate : "[% date.outputdate %]",
+ holiday_type:"[% date.holiday_type %]",
+ open_hour: "[% date.open_hour %]",
+ close_hour: "[% date.close_hour %]"
+ };
+ [% END %]
+
+ /**
+ * Displays the details of the selected date on a side panel
+ */
+ function showHoliday (date_obj, dateString, dayName, day, month, year, weekDay, title, holidayType) {
+ $("#newHoliday").slideDown("fast");
+ $("#copyHoliday").slideUp("fast");
+ $('#newDaynameOutput').html(dayName);
+ $('#newDayname').val(dayName);
+ $('#newBranchNameOutput').html($("#branch :selected").text());
+ $(".newHoliday ,#branch").val($('#branch').val());
+ $('#newDayOutput').html(day);
+ $(".newHoliday #Day").val(day);
+ $(".newHoliday #Month").val(month);
+ $(".newHoliday #Year").val(year);
+ $("#newMonthOutput").html(month);
+ $("#newYearOutput").html(year);
+ $(".newHoliday, #Weekday").val(weekDay);
+
+ $('.newHoliday #title').val(title);
+ $('#HolidayType').val(holidayType);
+ $('#days_of_week option[value="'+ (weekDay + 1) +'"]').attr('selected', true);
+ $('#openHour').val(datesInfos[dateString].open_hour);
+ $('#closeHour').val(datesInfos[dateString].close_hour);
+ $('#local_today').val(getSeparetedDate(new Date()).dateString);
+
+ //This changes the label of the date type on the edit panel
+ if(holidayType == 'W') {
+ $("#holtype").attr("class","key repeatableweekly").html(_("Holiday repeating weekly"));
+ } else if(holidayType == 'R') {
+ $("#holtype").attr("class","key repeatableyearly").html(_("Holiday repeating yearly"));
+ } else if(holidayType == 'F') {
+ $("#holtype").attr("class","key float").html(_("Floating holiday"));
+ } else if(holidayType == 'N') {
+ $("#holtype").attr("class","key exception").html(_("Needs validation"));
+ } else if(holidayType == 'E') {
+ $("#holtype").attr("class","key holiday").html(_("Unique holiday"));
+ } else{
+ $("#holtype").attr("class","key normalday").html(_("Working day "));
+ }
+
+ //Select the correct holiday type on the dropdown menu
+ if (datesInfos[dateString].holiday_type !=''){
+ var type = datesInfos[dateString].holiday_type;
+ $('#holidayType option[value="'+ type +'"]').attr('selected', true)
+ }else{
+ $('#holidayType option[value="none"]').attr('selected', true)
+ }
+
+ //If it is a weekly or repeatable holiday show the option to delete the type
+ if(datesInfos[dateString].holiday_type == 'W' || datesInfos[dateString].holiday_type == 'R'){
+ $('#deleteType').show("fast");
+ }else{
+ $('#deleteType').hide("fast");
+ }
+
+ //This value is to disable and hide input when the date is in the past, because you can't edit it.
+ var value = false;
+ var today = new Date();
+ today.setHours(0,0,0,0);
+ if(date_obj < today ){
+ $("#holtype").attr("class","key past-date").html(_("Past date"));
+ $("#CopyRadioButton").attr("checked", "checked");
+ value = true;
+ $(".CopyDatePanel").toggle(value);
+ }
+ $("#title").prop('disabled', value);
+ $("#holidayType select").prop('disabled', value);
+ $("#openHour").prop('disabled', value);
+ $("#closeHour").prop('disabled', value);
+ $("#EditRadioButton").parent().toggle(!value);
+
+ }
+
+ function hidePanel(aPanelName) {
+ $("#"+aPanelName).slideUp("fast");
+ }
+
+ function changeBranch () {
+ var branch = $("#branch option:selected").val();
+ location.href='/cgi-bin/koha/tools/discrete_calendar.pl?branch=' + branch + '&calendardate=' + "[% calendardate %]";
+ }
+
+ function Help() {
+ newin=window.open("/cgi-bin/koha/help.pl","KohaHelp",'width=600,height=600,toolbar=false,scrollbars=yes');
+ }
+
+ // This function gives css clases to each kind of day
+ function dateStatusHandler(date) {
+ date = getSeparetedDate(date);
+ var day = date.day;
+ var month = date.month;
+ var year = date.year;
+ var weekDay = date.weekDay;
+ var dayName = weekdays[weekDay];
+ var dateString = date.dateString;
+ var today = new Date();
+ today.setHours(0,0,0,0);
+
+ if (datesInfos[dateString] && datesInfos[dateString].holiday_type =='W'){
+ return [true, "repeatableweekly", _("Weekly holiday: %s").format(datesInfos[dateString].title)];
+ } else if (datesInfos[dateString] && datesInfos[dateString].holiday_type == 'R') {
+ return [true, "repeatableyearly", _("Yearly holiday: %s").format(datesInfos[dateString].title)];
+ } else if (datesInfos[dateString] && datesInfos[dateString].holiday_type == 'N') {
+ return [true, "exception", _("Need validation: %s").format(datesInfos[dateString].title)];
+ } else if (datesInfos[dateString] && datesInfos[dateString].holiday_type == 'F') {
+ return [true, "float", _("Floating holiday: %s").format(datesInfos[dateString].title)];
+ } else if (datesInfos[dateString] && datesInfos[dateString].holiday_type == 'E') {
+ return [true, "holiday", _("Single holiday: %s").format(datesInfos[dateString].title)];
+ } else {
+ if(date.date_obj < today ){
+ return [true, "past-date", _("Past day")];
+ }else{
+ return [true, "normalday", _("Normal day")];
+ }
+ }
+ }
+
+ /* This function is in charge of showing the correct panel considering the kind of holiday */
+ function dateChanged(text, date) {
+ date = getSeparetedDate(date);
+ var day = date.day;
+ var month = date.month;
+ var year = date.year;
+ var weekDay = date.weekDay;
+ var dayName = weekdays[weekDay];
+ var dateString = date.dateString;
+ var date_obj = date.date_obj;
+ //set value of form hidden field
+ $('#from_copyFrom').val(text);
+
+ showHoliday(date_obj, dateString, dayName, day, month, year, weekDay, datesInfos[dateString].title, datesInfos[dateString].holiday_type);
+ }
+
+ /**
+ * This function separate a given date object a returns an array containing all needed information about the date.
+ */
+ function getSeparetedDate(date){
+ var mydate = new Array();
+ var day = (date.getDate() < 10 ? '0' : '') + date.getDate();
+ var month = ((date.getMonth()+1) < 10 ? '0' : '') + (date.getMonth() +1);
+ var year = date.getFullYear();
+ var weekDay = date.getDay();
+ //iso date string
+ var dateString = year + '-' + month + '-' + day;
+ mydate = {
+ date_obj : date,
+ dateString : dateString,
+ weekDay: weekDay,
+ year: year,
+ month: month,
+ day: day
+ };
+
+ return mydate;
+ }
+
+ /**
+ * Valide the forms before send them to the backend
+ */
+ function validateForm(form){
+ if(form =='newHoliday' && $('#CopyRadioButton').is(':checked')){
+ if($('#to_copyFromDatePicker').val() =='' || $('#to_copyToDatePicker').val() ==''){
+ alert("You have to pick a FROM and TO in the Copy to different dates.");
+ return false;
+ }else if ($('#from_copyToDatePicker').val()){
+ var from_DateFrom = new Date($("#jcalendar-container").datepicker("getDate"));
+ var from_DateTo = new Date($('#from_copyToDatePicker').datepicker("getDate"));
+ var to_DateFrom = new Date($('#to_copyFromDatePicker').datepicker("getDate"));
+ var to_DateTo = new Date($('#to_copyToDatePicker').datepicker("getDate"));
+
+ var from_start = Math.round( from_DateFrom.getTime() / (3600*24*1000)); //days as integer from..
+ var from_end = Math.round( from_DateTo.getTime() / (3600*24*1000));
+ var to_start = Math.round( to_DateFrom.getTime() / (3600*24*1000));
+ var to_end = Math.round( to_DateTo.getTime() / (3600*24*1000));
+
+ var from_daysDiff = from_end - from_start +1;
+ var to_daysDiff = to_end - to_start + 1;
+ if(from_daysDiff == to_daysDiff){
+ $('#daysnumber').val(to_daysDiff);
+ return true;
+ }else{
+ alert("You have to pick the same number of days if you choose 2 ranges");
+ return false;
+ }
+ }
+ }else if(form == 'CopyCalendar'){
+ if ($('#newBranch').val() ==''){
+ alert("Please select a copy to calendar.");
+ return false;
+ }else{
+ return true;
+ }
+ }else {
+ return true;
+ }
+ }
+
+ function go_to_date(isoDate){
+ //I added the time to get around the timezone
+ var date = getSeparetedDate(new Date(isoDate + " 00:00:00"));
+ var day = date.day;
+ var month = date.month;
+ var year = date.year;
+ var weekDay = date.weekDay;
+ var dayName = weekdays[weekDay];
+ var dateString = date.dateString;
+ var date_obj = date.date_obj;
+
+ $("#jcalendar-container").datepicker("setDate", date_obj);
+ showHoliday(date_obj, dateString, dayName, day, month, year, weekDay, datesInfos[dateString].title, datesInfos[dateString].holiday_type);
+ }
+
+ /**
+ *Check if date range have the same opening, closing hours and holiday type if there's one.
+ */
+ function checkRange(date){
+ date = new Date(date);
+ $('#toDate').val(getSeparetedDate(date).dateString);
+ var fromDate = new Date($("#jcalendar-container").datepicker("getDate"));
+ var sameHoliday =true;
+ var sameOpenHours =true;
+ var sameCloseHours =true;
+
+ $('#days_of_week option[value="everyday"]').attr('selected', true);
+ for (var i = fromDate; i <= date ; i.setDate(i.getDate() + 1)) {
+ var myDate1 = getSeparetedDate(i);
+ var date1 = myDate1.dateString;
+ var holidayType1 = datesInfos[date1].holiday_type;
+ var open_hours1 = datesInfos[date1].open_hour;
+ var close_hours1 = datesInfos[date1].close_hour;
+ for (var j = fromDate; j <= date ; j.setDate(j.getDate() + 1)) {
+ var myDate2 = getSeparetedDate(j);
+ var date2 = myDate2.dateString;
+ var holidayType2 = datesInfos[date2].holiday_type;
+ var open_hours2 = datesInfos[date2].open_hour;
+ var close_hours2 = datesInfos[date2].close_hour;
+
+ if (sameHoliday && holidayType1 != holidayType2){
+ $('#holidayType option[value="empty"]').attr('selected', true);
+ sameHoliday=false;
+ }
+ if(sameOpenHours && (open_hours1 != open_hours2)){
+ $('#openHour').val('');
+ sameOpenHours=false;
+ }
+ if(sameCloseHours && (close_hours1 != close_hours2)){
+ $('#closeHour').val('');
+ sameCloseHours=false;
+ }
+ }
+ if (!sameOpenHours && !sameCloseHours && !sameHoliday){
+ return false;
+ }
+ }
+ return true;
+ }
+
+ $(document).ready(function() {
+ $(".hint").hide();
+ $("#branch").change(function(){
+ changeBranch();
+ });
+ $("#holidayweeklyrepeatable>tbody>tr").each(function(){
+ var first_td = $(this).find('td').first();
+ first_td.html(weekdays[first_td.html()]);
+ });
+ $("a.helptext").click(function(){
+ $(this).parent().find(".hint").toggle(); return false;
+ });
+ //Set the correct coloring, default date and the date ranges for all datepickers
+ $.datepicker.setDefaults({
+ beforeShowDay: function(thedate) {
+ return dateStatusHandler(thedate);
+ },
+ defaultDate: new Date("[% keydate %]"),
+ minDate: new Date("[% minDate %]"),
+ maxDate: new Date("[% maxDate %]"),
+ dateFormat: "yy-mm-dd"
+ });
+ //Main datepicker
+ $("#jcalendar-container").datepicker({
+ onSelect: function(dateText, inst) {
+ dateChanged(dateText, $(this).datepicker("getDate"));
+ },
+ });
+ $('#from_copyToDatePicker').datepicker();
+ $("#from_copyToDatePicker").change(function(){
+ checkRange($(this).datepicker("getDate"));
+ $('#from_copyTo').val(($(this).val()));
+ if($('#from_copyToDatePicker').val()){
+ $('#days_of_week').show("fast");
+ }else{
+ $('#days_of_week').hide("fast");
+ }
+ });
+ //Datepickers for copy dates feature
+ $('#to_copyFromDatePicker').datepicker();
+ $("#to_copyFromDatePicker").change(function(){
+ $('#to_copyFrom').val(($(this).val()));
+ });
+ $('#to_copyToDatePicker').datepicker();
+ $("#to_copyToDatePicker").change(function(){
+ $('#to_copyTo').val(($(this).val()));
+ });
+ //Timepickers for open and close hours
+ $('#openHour').timepicker({
+ showOn : 'focus',
+ timeFormat: 'HH:mm:ss',
+ showSecond: false,
+ stepMinute: 5,
+ });
+ $('#closeHour').timepicker({
+ showOn : 'focus',
+ timeFormat: 'HH:mm:ss',
+ showSecond: false,
+ stepMinute: 5,
+ });
+
+ $('.newHoliday input[type="radio"]').click(function () {
+ if ($(this).attr("id") == "CopyRadioButton") {
+ $(".CopyToBranchPanel").hide('fast');
+ $(".CopyDatePanel").show('fast');
+ } else if ($(this).attr("id") == "CopyToBranchRadioButton"){
+ $(".CopyDatePanel").hide('fast');
+ $(".CopyToBranchPanel").show('fast');
+ } else{
+ $(".CopyDatePanel").hide('fast');
+ $(".CopyToBranchPanel").hide('fast');
+ }
+ });
+
+ $(".hidePanel").on("click",function(){
+ if( $(this).hasClass("showHoliday") ){
+ hidePanel("showHoliday");
+ }if ($(this).hasClass('newHoliday')) {
+ hidePanel("newHoliday");
+ }else {
+ hidePanel("copyHoliday");
+ }
+ });
+
+ $("#deleteType_checkbox").on("change", function(){
+ if($("#deleteType_checkbox").is(':checked')){
+ $('#holidayType option[value="none"]').attr('selected', true);
+ }
+ });
+ $("#holidayType select").on("change", function(){
+ if($("#holidayType select").val() == "R"){
+ $('#days_of_week').hide("fast");
+ }else if ($('#from_copyToDatePicker').val()){
+ $('#days_of_week').show("fast");
+ }
+ });
+ });
+ //]]>
+</script>
+[% END %]
+[% INCLUDE 'intranet-bottom.inc' %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt
index 350fd1b28d..b448d83aca 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt
@@ -91,7 +91,7 @@
[% END %]
<dl>
[% IF ( CAN_user_tools_edit_calendar ) %]
- <dt><a href="/cgi-bin/koha/tools/holidays.pl">Calendar</a></dt>
+ <dt><a href="/cgi-bin/koha/tools/discrete_calendar.pl">Calendar</a></dt>
<dd>Define days when the library is closed</dd>
[% END %]
diff --git a/misc/cronjobs/add_days_discrete_calendar.pl b/misc/cronjobs/add_days_discrete_calendar.pl
new file mode 100755
index 0000000000..2e4f3a94da
--- /dev/null
+++ b/misc/cronjobs/add_days_discrete_calendar.pl
@@ -0,0 +1,128 @@
+#!/usr/bin/perl
+
+#
+# This script adds one day into discrete_calendar table based on the same day from the week before
+#
+use Modern::Perl;
+use DateTime;
+use DateTime::Format::Strptime;
+use Data::Dumper;
+use Getopt::Long;
+use C4::Context;
+use Koha::Database;
+use Koha::DiscreteCalendar;
+
+# Options
+my $help = 0;
+my $daysInFuture = 1;
+my $debug = 0;
+GetOptions (
+ 'help|?|h' => \$help,
+ 'n=i' => \$daysInFuture,
+ 'd|?|debug' => \$debug);
+
+my $usage = << 'ENDUSAGE';
+
+This script adds days into discrete_calendar table based on the same day from the week before.
+
+Examples :
+ The latest date on discrete_calendar is : 28-07-2017
+ The current date : 01-08-2016
+ The dates that will be added are : 29-07-2017, 30-07-2017, 31-07-2017, 01-08-2017
+Open close exemples :
+ Date added is : 29-07-2017
+ Opening/closing hours will be base on : 22-07-2017 (- 7 days)
+ Library open or closed will be based on : 29-07-2017 (- 1 year)
+This script has the following parameters:
+ -h --help: this message
+ -n : number of days to add in the futre, default : 1
+ -d --debug: displays all added days and errors if there is any
+
+ENDUSAGE
+
+if ($help) {
+ print $usage;
+ exit;
+}
+
+my $schema = Koha::Database->new->schema;
+$schema->storage->txn_begin;
+my $dbh = C4::Context->dbh;
+
+# Predeclaring variables that will be used several times in the code
+my $query;
+my $statement;
+
+#getting the all the branches
+$query = 'SELECT branchcode FROM discrete_calendar GROUP BY branchcode';
+$statement = $dbh->prepare($query);
+$statement->execute();
+my @branches = ();
+for my $branchcode ($statement->fetchrow_array ) {
+ push @branches,$branchcode;
+}
+
+#get the latest date in the table
+$query = "SELECT MAX(date) FROM discrete_calendar";
+$statement = $dbh->prepare($query);
+$statement->execute();
+my $latestedDate = $statement->fetchrow_array;
+my $parser = DateTime::Format::Strptime->new(
+ pattern => '%Y-%m-%d %H:%M:%S',
+ on_error => 'croak',
+);
+
+$latestedDate = $parser->parse_datetime($latestedDate);
+
+my $newDay = $latestedDate->clone();
+$latestedDate->add(days => $daysInFuture);
+
+for ($newDay->add(days => 1); $newDay <= $latestedDate; $newDay->add(days => 1)) {
+ my $lastWeekDay = $newDay->clone();
+ $lastWeekDay->add(days=> -8);
+ my $dayOfWeek = $lastWeekDay->day_of_week;
+ # Representation fix
+ # DateTime object dow (1-7) where Monday is 1
+ # Arrays are 0-based where 0 = Sunday, not 7.
+ $dayOfWeek -= 1 unless $dayOfWeek == 7;
+ $dayOfWeek = 0 if $dayOfWeek == 7;
+
+ #checking if it was open on the same day from last year
+ my $yearAgo = $newDay->clone();
+ $yearAgo = $yearAgo->add(years => -1);
+ my $last_year = 'SELECT is_opened, holiday_type, note FROM discrete_calendar WHERE date=? AND branchcode=?';
+ my $day_last_week = "SELECT open_hour, close_hour FROM discrete_calendar WHERE DAYOFWEEK(date)=DAYOFWEEK(?) and date < ? order by date desc limit 1";
+ my $add_Day = 'INSERT INTO discrete_calendar (date,branchcode,is_opened,open_hour,close_hour) VALUES (?,?,?,?,?)';
+ my $note ='';
+ #insert into discrete_calendar for each branch
+ foreach my $branchCode(@branches){
+ $statement = $dbh->prepare($last_year);
+ $statement->execute($yearAgo,$branchCode);
+ my ($is_opened, $holiday_type, $note) = $statement->fetchrow_array;
+ #weekly and unique holidays are not replicated in the future
+ if ($holiday_type && $holiday_type ne "R"){
+ $is_opened = 1;
+ if ($holiday_type eq "W" || $holiday_type eq "E"){
+ $holiday_type='';
+ $note='';
+ }elsif ($holiday_type eq "F"){
+ $holiday_type = 'N';
+ }
+ }
+ $holiday_type = '' if $is_opened;
+ $statement = $dbh->prepare($day_last_week);
+ $statement->execute($newDay, $newDay);
+ my ($open_hour,$close_hour ) = $statement->fetchrow_array;
+ my $add_Day = 'INSERT INTO discrete_calendar (date,branchcode,is_opened,holiday_type, note,open_hour,close_hour) VALUES (?,?,?,?,?,?,?)';
+ $statement = $dbh->prepare($add_Day);
+ $statement->execute($newDay,$branchCode,$is_opened,$holiday_type,$note, $open_hour,$close_hour);
+
+ if($debug && !$@){
+ warn "Added day $newDay to $branchCode is opened : $is_opened, holiday_type : $holiday_type, note: $note, open_hour : $open_hour, close_hour : $close_hour \n";
+ }elsif($@){
+ warn "Failed to add day $newDay to $branchCode : $_\n";
+ }
+ }
+}
+# If everything went well we commit to the database
+$schema->storage->txn_commit;
\ No newline at end of file
diff --git a/misc/cronjobs/fines.pl b/misc/cronjobs/fines.pl
index 9f61c0f598..c22b8d55f7 100755
--- a/misc/cronjobs/fines.pl
+++ b/misc/cronjobs/fines.pl
@@ -38,7 +38,7 @@ use Carp;
use File::Spec;
use Try::Tiny;
-use Koha::Calendar;
+use Koha::DiscreteCalendar;
use Koha::DateUtils;
use Koha::Patrons;
use C4::Log;
@@ -196,7 +196,7 @@ EOM
sub set_holiday {
my ( $branch, $dt ) = @_;
- my $calendar = Koha::Calendar->new( branchcode => $branch );
+ my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branch });
return $calendar->is_holiday($dt);
}
diff --git a/misc/cronjobs/holds/cancel_unfilled_holds.pl b/misc/cronjobs/holds/cancel_unfilled_holds.pl
index ba6a40098a..a957a9a661 100755
--- a/misc/cronjobs/holds/cancel_unfilled_holds.pl
+++ b/misc/cronjobs/holds/cancel_unfilled_holds.pl
@@ -32,7 +32,7 @@ use Koha::Script -cron;
use C4::Reserves;
use C4::Log;
use Koha::Holds;
-use Koha::Calendar;
+use Koha::DiscreteCalendar;
use Koha::DateUtils;
use Koha::Libraries;
diff --git a/misc/cronjobs/overdue_notices.pl b/misc/cronjobs/overdue_notices.pl
index 01200d8435..11fc79f15d 100755
--- a/misc/cronjobs/overdue_notices.pl
+++ b/misc/cronjobs/overdue_notices.pl
@@ -41,7 +41,7 @@ use C4::Overdues qw(GetFine GetOverdueMessageTransportTypes parse_overdues_lette
use C4::Log;
use Koha::Patron::Debarments qw(AddUniqueDebarment);
use Koha::DateUtils;
-use Koha::Calendar;
+use Koha::DiscreteCalendar;
use Koha::Libraries;
use Koha::Acquisition::Currencies;
use Koha::Patrons;
@@ -444,9 +444,8 @@ elsif ( defined $text_filename ) {
}
foreach my $branchcode (@branches) {
- my $calendar;
if ( C4::Context->preference('OverdueNoticeCalendar') ) {
- $calendar = Koha::Calendar->new( branchcode => $branchcode );
+ my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branchcode });
if ( $calendar->is_holiday($date_to_run) ) {
next;
}
@@ -549,13 +548,11 @@ END_SQL
my $days_between;
if ( C4::Context->preference('OverdueNoticeCalendar') )
{
- $days_between =
- $calendar->days_between( dt_from_string($data->{date_due}),
- $date_to_run );
+ my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branchcode });
+ $days_between = $calendar->days_between( dt_from_string($data->{date_due}), $date_to_run );
}
else {
- $days_between =
- $date_to_run->delta_days( dt_from_string($data->{date_due}) );
+ $days_between = $date_to_run->delta_days( dt_from_string($data->{date_due}) );
}
$days_between = $days_between->in_units('days');
if ($triggered) {
@@ -632,9 +629,8 @@ END_SQL
my $exceededPrintNoticesMaxLines = 0;
while ( my $item_info = $sth2->fetchrow_hashref() ) {
if ( C4::Context->preference('OverdueNoticeCalendar') ) {
- $days_between =
- $calendar->days_between(
- dt_from_string( $item_info->{date_due} ), $date_to_run );
+ my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branchcode });
+ $days_between = $calendar->days_between( dt_from_string( $item_info->{date_due} ), $date_to_run );
}
else {
$days_between =
diff --git a/misc/cronjobs/staticfines.pl b/misc/cronjobs/staticfines.pl
index 1e7d287702..cb0f031200 100755
--- a/misc/cronjobs/staticfines.pl
+++ b/misc/cronjobs/staticfines.pl
@@ -41,7 +41,7 @@ use Koha::Script -cron;
use C4::Context;
use C4::Circulation;
use C4::Overdues;
-use C4::Calendar qw(); # don't need any exports from Calendar
+use Koha::DiscreteCalendar qw(); # don't need any exports from Calendar
use C4::Biblio;
use C4::Debug; # supplying $debug and $cgi_debug
use C4::Log;
@@ -177,7 +177,7 @@ for ( my $i = 0 ; $i < scalar(@$data) ; $i++ ) {
my $calendar;
unless ( defined( $calendars{$branchcode} ) ) {
- $calendars{$branchcode} = C4::Calendar->new( branchcode => $branchcode );
+ $calendars{$branchcode} = Koha::DiscreteCalendar->new({ branchcode => $branchcode });
}
$calendar = $calendars{$branchcode};
my $isHoliday = $calendar->isHoliday( $tday, $tmonth, $tyear );
diff --git a/misc/cronjobs/thirdparty/TalkingTech_itiva_outbound.pl b/misc/cronjobs/thirdparty/TalkingTech_itiva_outbound.pl
index dd4d2f9ff2..02cabdd6ab 100755
--- a/misc/cronjobs/thirdparty/TalkingTech_itiva_outbound.pl
+++ b/misc/cronjobs/thirdparty/TalkingTech_itiva_outbound.pl
@@ -36,7 +36,7 @@ use C4::Context;
use C4::Items;
use C4::Letters;
use C4::Overdues;
-use Koha::Calendar;
+use Koha::DiscreteCalendar;
use Koha::DateUtils;
use Koha::Patrons;
use Koha::Libraries;
@@ -341,7 +341,7 @@ sub GetWaitingHolds {
}
);
- my $calendar = Koha::Calendar->new( branchcode => $issue->{'site'}, days_mode => $daysmode );
+ my $calendar = Koha::DiscreteCalendar->new( branchcode => $issue->{'site'}, days_mode => $daysmode );
my $waiting_date = dt_from_string( $issue->{waitingdate}, 'sql' );
my $pickup_date = $waiting_date->clone->add( days => $pickupdelay );
diff --git a/tools/copy-holidays.pl b/tools/copy-holidays.pl
deleted file mode 100755
index 6e35451226..0000000000
--- a/tools/copy-holidays.pl
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/perl
-
-# Copyright 2012 Catalyst IT
-#
-# 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 <http://www.gnu.org/licenses>.
-
-use Modern::Perl;
-
-use CGI qw ( -utf8 );
-
-use C4::Auth;
-use C4::Output;
-
-
-use C4::Calendar;
-
-my $input = new CGI;
-my $dbh = C4::Context->dbh();
-
-checkauth($input, 0, {tools=> 'edit_calendar'}, 'intranet');
-
-my $branchcode = $input->param('branchcode');
-my $from_branchcode = $input->param('from_branchcode');
-
-C4::Calendar->new(branchcode => $from_branchcode)->copy_to_branch($branchcode) if $from_branchcode && $branchcode;
-
-print $input->redirect("/cgi-bin/koha/tools/holidays.pl?branch=".($branchcode || $from_branchcode));
diff --git a/tools/discrete_calendar.pl b/tools/discrete_calendar.pl
new file mode 100755
index 0000000000..fa40c9b575
--- /dev/null
+++ b/tools/discrete_calendar.pl
@@ -0,0 +1,155 @@
+#!/usr/bin/perl
+
+# 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 <http://www.gnu.org/licenses>.
+
+#####Sets holiday periods for each branch. Datedues will be extended if branch is closed -TG
+use Modern::Perl;
+
+use CGI qw ( -utf8 );
+
+use C4::Auth;
+use C4::Output;
+
+use Koha::DateUtils;
+use Koha::DiscreteCalendar;
+
+my $input = new CGI;
+
+# Get the template to use
+my ($template, $loggedinuser, $cookie)
+ = get_template_and_user({template_name => "tools/discrete_calendar.tt",
+ type => "intranet",
+ query => $input,
+ authnotrequired => 0,
+ flagsrequired => {tools => 'edit_calendar'},
+ debug => 1,
+ });
+
+my $branch = $input->param('branch') || C4::Context->userenv->{'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};
+
+my $weekday = $input->param('day_of_week');
+
+my $holiday_type = $input->param('holidayType');
+my $allbranches = $input->param('allBranches');
+
+my $title = $input->param('Title');
+
+my $action = $input->param('action') || '';
+
+# calendardate - date passed in url for human readability (syspref)
+# if the url has an invalid date default to 'now.'
+my $calendarinput_dt = eval { dt_from_string( scalar $input->param('calendardate')); } || dt_from_string;
+my $calendardate = output_pref( { dt => $calendarinput_dt, dateonly => 1 } );
+
+if($action eq 'copyBranch'){
+ $calendar->copy_to_branch(scalar $input->param('newBranch'));
+} elsif($action eq 'copyDates'){
+ my $from_startDate = $input->param('from_copyFrom') ||'';
+ my $from_endDate = $input->param('toDate') || '';
+ my $to_startDate = $input->param('to_copyFrom') || '';
+ my $to_endDate = $input->param('to_copyTo') || '';
+ my $daysnumber= $input->param('daysnumber');
+
+ $from_startDate = dt_from_string(scalar $from_startDate) if$from_startDate ne '';
+ $to_startDate = dt_from_string(scalar $to_startDate) if $to_startDate ne '';
+ $to_startDate = dt_from_string(scalar $to_startDate) if $to_startDate ne '';
+ $to_endDate = dt_from_string(scalar $to_endDate) if $to_endDate ne '';
+
+ $calendar->copy_holiday($from_startDate, $from_endDate, $to_startDate, $to_endDate, $daysnumber);
+} elsif($action eq 'edit'){
+ my $openHour = $input->param('openHour');
+ my $closeHour = $input->param('closeHour');
+ my $endDate = $input->param('toDate');
+ my $deleteType = $input->param('deleteType') || 0;
+ #Get today from javascript for a precise local time
+ my $local_today = dt_from_string( $input->param('local_today'), 'iso');
+
+ my $startDate = dt_from_string(scalar $input->param('from_copyFrom'));
+
+ if($endDate ne '' ) {
+ $endDate = dt_from_string(scalar $endDate);
+ } else{
+ $endDate = $startDate->clone();
+ }
+
+ warn $startDate;
+ warn $endDate;
+ $calendar->edit_holiday( {
+ title => $title,
+ weekday => $weekday,
+ holiday_type => $holiday_type,
+ open_hour => $openHour,
+ close_hour => $closeHour,
+ start_date => $startDate,
+ end_date => $endDate,
+ delete_type => $deleteType,
+ today => $local_today
+ });
+}
+
+# keydate - date passed to calendar.js. calendar.js does not process dashes within a date.
+
+my $keydate = output_pref( { dt => $calendarinput_dt, dateonly => 1, dateformat => 'iso' } );
+$keydate =~ s/-/\//g;
+
+# Set all the branches.
+if ( C4::Context->only_my_library ) {
+ $branch = C4::Context->userenv->{'branch'};
+}
+
+# Get all the holidays
+
+#discrete_calendar weekly holidays
+my @week_days = $calendar->get_week_days_holidays();
+
+#discrete_calendar repeatable holidays
+my @repeatable_holidays = $calendar->get_repeatable_holidays();
+
+#discrete_calendar unique holidays
+my @unique_holidays =$calendar->get_unique_holidays();
+#discrete_calendar floating holidays
+my @float_holidays =$calendar->get_float_holidays();
+#discrete_caledar need validation holidays
+my @need_validation_holidays =$calendar->get_need_validation_holidays();
+
+#Calendar maximum date
+my $minDate = $calendar->get_min_date($branch);
+
+#Calendar minimum date
+my $maxDate = $calendar->get_max_date($branch);
+
+my @datesInfos = $calendar->get_dates_info($branch);
+
+$template->param(
+ UNIQUE_HOLIDAYS => \@unique_holidays,
+ FLOAT_HOLIDAYS => \@float_holidays,
+ NEED_VALIDATION_HOLIDAYS => \@need_validation_holidays,
+ REPEATABLE_HOLIDAYS => \@repeatable_holidays,
+ WEEKLY_HOLIDAYS => \@week_days,
+ calendardate => $calendardate,
+ keydate => $keydate,
+ branch => $branch,
+ minDate => $minDate,
+ maxDate => $maxDate,
+ datesInfos => \@datesInfos,
+ no_branch_selected => $no_branch_selected,
+);
+
+# Shows the template with the real values replaced
+output_html_with_http_headers $input, $cookie, $template->output;
diff --git a/tools/exceptionHolidays.pl b/tools/exceptionHolidays.pl
deleted file mode 100755
index 7095157617..0000000000
--- a/tools/exceptionHolidays.pl
+++ /dev/null
@@ -1,143 +0,0 @@
-#!/usr/bin/perl
-
-use Modern::Perl;
-
-use CGI qw ( -utf8 );
-
-use C4::Auth;
-use C4::Output;
-use DateTime;
-
-use C4::Calendar;
-use Koha::DateUtils;
-
-my $input = new CGI;
-my $dbh = C4::Context->dbh();
-
-checkauth($input, 0, {tools=> 'edit_calendar'}, 'intranet');
-
-
-our $branchcode = $input->param('showBranchName');
-my $originalbranchcode = $branchcode;
-our $weekday = $input->param('showWeekday');
-our $day = $input->param('showDay');
-our $month = $input->param('showMonth');
-our $year = $input->param('showYear');
-our $title = $input->param('showTitle');
-our $description = $input->param('showDescription');
-our $holidaytype = $input->param('showHolidayType');
-my $datecancelrange_dt = eval { dt_from_string( scalar $input->param('datecancelrange') ) };
-my $calendardate = sprintf("%04d-%02d-%02d", $year, $month, $day);
-our $showoperation = $input->param('showOperation');
-my $allbranches = $input->param('allBranches');
-
-$title || ($title = '');
-if ($description) {
- $description =~ s/\r/\\r/g;
- $description =~ s/\n/\\n/g;
-} else {
- $description = '';
-}
-
-# We make an array with holiday's days
-our @holiday_list;
-if ($datecancelrange_dt){
- my $first_dt = DateTime->new(year => $year, month => $month, day => $day);
-
- for (my $dt = $first_dt->clone();
- $dt <= $datecancelrange_dt;
- $dt->add(days => 1) )
- {
- push @holiday_list, $dt->clone();
- }
-}
-
-if($allbranches) {
- my $libraries = Koha::Libraries->search;
- while ( my $library = $libraries->next ) {
- edit_holiday($showoperation, $library->branchcode, $weekday, $day, $month, $year, $title, $description, $holidaytype, @holiday_list);
- }
-} else {
- edit_holiday($showoperation, $branchcode, $weekday, $day, $month, $year, $title, $description, $holidaytype, @holiday_list);
-}
-
-print $input->redirect("/cgi-bin/koha/tools/holidays.pl?branch=$originalbranchcode&calendardate=$calendardate");
-
-sub edit_holiday {
- ($showoperation, $branchcode, $weekday, $day, $month, $year, $title, $description, $holidaytype, @holiday_list) = @_;
- my $calendar = C4::Calendar->new(branchcode => $branchcode);
-
- if ($showoperation eq 'exception') {
- $calendar->insert_exception_holiday(day => $day,
- month => $month,
- year => $year,
- title => $title,
- description => $description);
- } elsif ($showoperation eq 'exceptionrange' ) {
- if (@holiday_list){
- foreach my $date (@holiday_list){
- $calendar->insert_exception_holiday(
- day => $date->{local_c}->{day},
- month => $date->{local_c}->{month},
- year => $date->{local_c}->{year},
- title => $title,
- description => $description
- );
- }
- }
- } elsif ($showoperation eq 'edit') {
- if($holidaytype eq 'weekday') {
- $calendar->ModWeekdayholiday(weekday => $weekday,
- title => $title,
- description => $description);
- } elsif ($holidaytype eq 'daymonth') {
- $calendar->ModDaymonthholiday(day => $day,
- month => $month,
- title => $title,
- description => $description);
- } elsif ($holidaytype eq 'ymd') {
- $calendar->ModSingleholiday(day => $day,
- month => $month,
- year => $year,
- title => $title,
- description => $description);
- } elsif ($holidaytype eq 'exception') {
- $calendar->ModExceptionholiday(day => $day,
- month => $month,
- year => $year,
- title => $title,
- description => $description);
- }
- } elsif ($showoperation eq 'delete') {
- $calendar->delete_holiday(weekday => $weekday,
- day => $day,
- month => $month,
- year => $year);
- }elsif ($showoperation eq 'deleterange') {
- if (@holiday_list){
- foreach my $date (@holiday_list){
- $calendar->delete_holiday_range(weekday => $weekday,
- day => $date->{local_c}->{day},
- month => $date->{local_c}->{month},
- year => $date->{local_c}->{year});
- }
- }
- }elsif ($showoperation eq 'deleterangerepeat') {
- if (@holiday_list){
- foreach my $date (@holiday_list){
- $calendar->delete_holiday_range_repeatable(weekday => $weekday,
- day => $date->{local_c}->{day},
- month => $date->{local_c}->{month});
- }
- }
- }elsif ($showoperation eq 'deleterangerepeatexcept') {
- if (@holiday_list){
- foreach my $date (@holiday_list){
- $calendar->delete_exception_holiday_range(weekday => $weekday,
- day => $date->{local_c}->{day},
- month => $date->{local_c}->{month},
- year => $date->{local_c}->{year});
- }
- }
- }
-}
diff --git a/tools/holidays.pl b/tools/holidays.pl
deleted file mode 100755
index 2a9f02dfb2..0000000000
--- a/tools/holidays.pl
+++ /dev/null
@@ -1,131 +0,0 @@
-#!/usr/bin/perl
-
-# 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 <http://www.gnu.org/licenses>.
-
-#####Sets holiday periods for each branch. Datedues will be extended if branch is closed -TG
-use Modern::Perl;
-
-use CGI qw ( -utf8 );
-
-use C4::Auth;
-use C4::Output;
-
-use C4::Calendar;
-use Koha::DateUtils;
-
-my $input = new CGI;
-
-my $dbh = C4::Context->dbh();
-# Get the template to use
-my ($template, $loggedinuser, $cookie)
- = get_template_and_user({template_name => "tools/holidays.tt",
- type => "intranet",
- query => $input,
- flagsrequired => {tools => 'edit_calendar'},
- debug => 1,
- });
-
-# calendardate - date passed in url for human readability (syspref)
-# if the url has an invalid date default to 'now.'
-my $calendarinput_dt = eval { dt_from_string( scalar $input->param('calendardate') ); } || dt_from_string;
-my $calendardate = output_pref( { dt => $calendarinput_dt, dateonly => 1 } );
-
-# keydate - date passed to calendar.js. calendar.js does not process dashes within a date.
-my $keydate = output_pref( { dt => $calendarinput_dt, dateonly => 1, dateformat => 'iso' } );
-$keydate =~ s/-/\//g;
-
-my $branch= $input->param('branch') || C4::Context->userenv->{'branch'};
-
-# Get all the holidays
-
-my $calendar = C4::Calendar->new(branchcode => $branch);
-my $week_days_holidays = $calendar->get_week_days_holidays();
-my @week_days;
-foreach my $weekday (keys %$week_days_holidays) {
-# warn "WEEK DAY : $weekday";
- my %week_day;
- %week_day = (KEY => $weekday,
- TITLE => $week_days_holidays->{$weekday}{title},
- DESCRIPTION => $week_days_holidays->{$weekday}{description});
- push @week_days, \%week_day;
-}
-
-my $day_month_holidays = $calendar->get_day_month_holidays();
-my @day_month_holidays;
-foreach my $monthDay (keys %$day_month_holidays) {
- # Determine date format on month and day.
- my $day_monthdate;
- my $day_monthdate_sort;
- if (C4::Context->preference("dateformat") eq "metric") {
- $day_monthdate_sort = "$day_month_holidays->{$monthDay}{month}-$day_month_holidays->{$monthDay}{day}";
- $day_monthdate = "$day_month_holidays->{$monthDay}{day}/$day_month_holidays->{$monthDay}{month}";
- } elsif (C4::Context->preference("dateformat") eq "dmydot") {
- $day_monthdate_sort = "$day_month_holidays->{$monthDay}{month}.$day_month_holidays->{$monthDay}{day}";
- $day_monthdate = "$day_month_holidays->{$monthDay}{day}.$day_month_holidays->{$monthDay}{month}";
- }elsif (C4::Context->preference("dateformat") eq "us") {
- $day_monthdate = "$day_month_holidays->{$monthDay}{month}/$day_month_holidays->{$monthDay}{day}";
- $day_monthdate_sort = $day_monthdate;
- } else {
- $day_monthdate = "$day_month_holidays->{$monthDay}{month}-$day_month_holidays->{$monthDay}{day}";
- $day_monthdate_sort = $day_monthdate;
- }
- my %day_month;
- %day_month = (KEY => $monthDay,
- DATE_SORT => $day_monthdate_sort,
- DATE => $day_monthdate,
- TITLE => $day_month_holidays->{$monthDay}{title},
- DESCRIPTION => $day_month_holidays->{$monthDay}{description});
- push @day_month_holidays, \%day_month;
-}
-
-my $exception_holidays = $calendar->get_exception_holidays();
-my @exception_holidays;
-foreach my $yearMonthDay (keys %$exception_holidays) {
- my $exceptiondate = eval { dt_from_string( $exception_holidays->{$yearMonthDay}{date} ) };
- my %exception_holiday;
- %exception_holiday = (KEY => $yearMonthDay,
- DATE_SORT => $exception_holidays->{$yearMonthDay}{date},
- DATE => output_pref( { dt => $exceptiondate, dateonly => 1 } ),
- TITLE => $exception_holidays->{$yearMonthDay}{title},
- DESCRIPTION => $exception_holidays->{$yearMonthDay}{description});
- push @exception_holidays, \%exception_holiday;
-}
-
-my $single_holidays = $calendar->get_single_holidays();
-my @holidays;
-foreach my $yearMonthDay (keys %$single_holidays) {
- my $holidaydate_dt = eval { dt_from_string( $single_holidays->{$yearMonthDay}{date} ) };
- my %holiday;
- %holiday = (KEY => $yearMonthDay,
- DATE_SORT => $single_holidays->{$yearMonthDay}{date},
- DATE => output_pref( { dt => $holidaydate_dt, dateonly => 1 } ),
- TITLE => $single_holidays->{$yearMonthDay}{title},
- DESCRIPTION => $single_holidays->{$yearMonthDay}{description});
- push @holidays, \%holiday;
-}
-
-$template->param(
- WEEK_DAYS_LOOP => \@week_days,
- HOLIDAYS_LOOP => \@holidays,
- EXCEPTION_HOLIDAYS_LOOP => \@exception_holidays,
- DAY_MONTH_HOLIDAYS_LOOP => \@day_month_holidays,
- calendardate => $calendardate,
- keydate => $keydate,
- branch => $branch,
-);
-
-# Shows the template with the real values replaced
-output_html_with_http_headers $input, $cookie, $template->output;
diff --git a/tools/newHolidays.pl b/tools/newHolidays.pl
deleted file mode 100755
index 7644006a2c..0000000000
--- a/tools/newHolidays.pl
+++ /dev/null
@@ -1,144 +0,0 @@
-#!/usr/bin/perl
-#FIXME: perltidy this file
-
-# 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 Lic# along with Koha; if not, see <http://www.gnu.org/licenses>.
-# along with Koha; if not, see <http://www.gnu.org/licenses>.
-
-
-use Modern::Perl;
-
-use CGI qw ( -utf8 );
-
-use C4::Auth;
-use C4::Output;
-
-use C4::Calendar;
-use DateTime;
-use Koha::DateUtils;
-
-my $input = new CGI;
-my $dbh = C4::Context->dbh();
-
-checkauth($input, 0, {tools=> 'edit_calendar'}, 'intranet');
-
-our $branchcode = $input->param('newBranchName');
-my $originalbranchcode = $branchcode;
-our $weekday = $input->param('newWeekday');
-our $day = $input->param('newDay');
-our $month = $input->param('newMonth');
-our $year = $input->param('newYear');
-my $dateofrange = $input->param('dateofrange');
-our $title = $input->param('newTitle');
-our $description = $input->param('newDescription');
-our $newoperation = $input->param('newOperation');
-my $allbranches = $input->param('allBranches');
-
-
-my $first_dt = DateTime->new(year => $year, month => $month, day => $day);
-my $end_dt = eval { dt_from_string( $dateofrange ); };
-
-my $calendardate = output_pref( { dt => $first_dt, dateonly => 1, dateformat => 'iso' } );
-
-$title || ($title = '');
-if ($description) {
- $description =~ s/\r/\\r/g;
- $description =~ s/\n/\\n/g;
-} else {
- $description = '';
-}
-
-# We make an array with holiday's days
-our @holiday_list;
-if ($end_dt){
- for (my $dt = $first_dt->clone();
- $dt <= $end_dt;
- $dt->add(days => 1) )
- {
- push @holiday_list, $dt->clone();
- }
-}
-
-if($allbranches) {
- my $libraries = Koha::Libraries->search;
- while ( my $library = $libraries->next ) {
- add_holiday($newoperation, $library->branchcode, $weekday, $day, $month, $year, $title, $description);
- }
-} else {
- add_holiday($newoperation, $branchcode, $weekday, $day, $month, $year, $title, $description);
-}
-
-print $input->redirect("/cgi-bin/koha/tools/holidays.pl?branch=$originalbranchcode&calendardate=$calendardate");
-
-#FIXME: move add_holiday() to a better place
-sub add_holiday {
- ($newoperation, $branchcode, $weekday, $day, $month, $year, $title, $description) = @_;
- my $calendar = C4::Calendar->new(branchcode => $branchcode);
-
- if ($newoperation eq 'weekday') {
- unless ( $weekday && ($weekday ne '') ) {
- # was dow calculated by javascript? original code implies it was supposed to be.
- # if not, we need it.
- $weekday = &Date::Calc::Day_of_Week($year, $month, $day) % 7 unless($weekday);
- }
- unless($calendar->isHoliday($day, $month, $year)) {
- $calendar->insert_week_day_holiday(weekday => $weekday,
- title => $title,
- description => $description);
- }
- } elsif ($newoperation eq 'repeatable') {
- unless($calendar->isHoliday($day, $month, $year)) {
- $calendar->insert_day_month_holiday(day => $day,
- month => $month,
- title => $title,
- description => $description);
- }
- } elsif ($newoperation eq 'holiday') {
- unless($calendar->isHoliday($day, $month, $year)) {
- $calendar->insert_single_holiday(day => $day,
- month => $month,
- year => $year,
- title => $title,
- description => $description);
- }
-
- } elsif ( $newoperation eq 'holidayrange' ) {
- if (@holiday_list){
- foreach my $date (@holiday_list){
- unless ( $calendar->isHoliday( $date->{local_c}->{day}, $date->{local_c}->{month}, $date->{local_c}->{year} ) ) {
- $calendar->insert_single_holiday(
- day => $date->{local_c}->{day},
- month => $date->{local_c}->{month},
- year => $date->{local_c}->{year},
- title => $title,
- description => $description
- );
- }
- }
- }
- } elsif ( $newoperation eq 'holidayrangerepeat' ) {
- if (@holiday_list){
- foreach my $date (@holiday_list){
- unless ( $calendar->isHoliday( $date->{local_c}->{day}, $date->{local_c}->{month}, $date->{local_c}->{year} ) ) {
- $calendar->insert_day_month_holiday(
- day => $date->{local_c}->{day},
- month => $date->{local_c}->{month},
- title => $title,
- description => $description
- );
- }
- }
- }
- }
-}
--
2.17.1