Bugzilla – Attachment 131635 Details for
Bug 17015
New Koha Calendar
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 17015: DiscreteCalendar UI, Back-End and necessary scripts
Bug-17015-DiscreteCalendar-UI-Back-End-and-necessa.patch (text/plain), 214.55 KB, created by
Michal Denar
on 2022-03-11 21:54:44 UTC
(
hide
)
Description:
Bug 17015: DiscreteCalendar UI, Back-End and necessary scripts
Filename:
MIME Type:
Creator:
Michal Denar
Created:
2022-03-11 21:54:44 UTC
Size:
214.55 KB
patch
obsolete
>From 3d406debfc9090b91eb04e4efb48f360a82793b6 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 > >Signed-off-by: Michal Denar <black23@gmail.com> >--- > C4/Calendar.pm | 736 --------- > C4/Circulation.pm | 13 +- > C4/HoldsQueue.pm | 11 +- > C4/Overdues.pm | 8 +- > C4/Reserves.pm | 4 +- > Koha/Calendar.pm | 555 ------- > Koha/Charges/Fees.pm | 4 +- > Koha/DiscreteCalendar.pm | 1332 +++++++++++++++++ > Koha/Hold.pm | 6 +- > circ/returns.pl | 3 +- > .../prog/css/discretecalendar.css | 213 +++ > .../prog/en/includes/tools-menu.inc | 2 +- > .../en/modules/tools/discrete_calendar.tt | 708 +++++++++ > .../prog/en/modules/tools/holidays.tt | 652 -------- > .../prog/en/modules/tools/tools-home.tt | 2 +- > misc/cronjobs/add_days_discrete_calendar.pl | 165 ++ > misc/cronjobs/fines.pl | 4 +- > misc/cronjobs/holds/cancel_unfilled_holds.pl | 3 +- > misc/cronjobs/overdue_notices.pl | 18 +- > misc/cronjobs/staticfines.pl | 7 +- > .../thirdparty/TalkingTech_itiva_outbound.pl | 4 +- > tools/copy-holidays.pl | 40 - > tools/discrete_calendar.pl | 175 +++ > tools/exceptionHolidays.pl | 143 -- > tools/holidays.pl | 130 -- > tools/newHolidays.pl | 144 -- > 26 files changed, 2644 insertions(+), 2438 deletions(-) > delete mode 100644 C4/Calendar.pm > delete mode 100644 Koha/Calendar.pm > create mode 100644 Koha/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 > delete mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/tools/holidays.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 257119c8e3..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 qw( croak ); >-use Date::Calc qw( 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 bbddb00fb9..503d43e7e9 100644 >--- a/C4/Circulation.pm >+++ b/C4/Circulation.pm >@@ -43,7 +43,7 @@ use Koha::Account; > use Koha::AuthorisedValues; > use Koha::Biblioitems; > use Koha::DateUtils qw( dt_from_string output_pref ); >-use Koha::Calendar; >+use Koha::DiscreteCalendar; > use Koha::Checkouts; > use Koha::Illrequests; > use Koha::Items; >@@ -1344,7 +1344,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, $patron->unblessed ); > >@@ -2477,7 +2477,7 @@ sub _calculate_new_debar_dt { > my $new_debar_dt; > # Use the calendar or not to calculate the debarment date > if ( C4::Context->preference('SuspensionsCalendar') eq 'noSuspensionsWhenClosed' ) { >- my $calendar = Koha::Calendar->new( >+ my $calendar = Koha::DiscreteCalendar->new( > branchcode => $branchcode, > days_mode => 'Calendar' > ); >@@ -3593,7 +3593,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->addDuration( $datedue, $dur, $loanlength->{lengthunit} ); > if ($loanlength->{lengthunit} eq 'days') { > $datedue->set_hour(23); >@@ -3632,14 +3632,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 b7cf0c7027..8e61565286 100644 >--- a/C4/HoldsQueue.pm >+++ b/C4/HoldsQueue.pm >@@ -32,6 +32,8 @@ use Koha::Libraries; > > use List::Util qw( shuffle ); > use List::MoreUtils qw( any ); >+use Koha::DiscreteCalendar; >+use Data::Dumper; > > our (@ISA, @EXPORT_OK); > BEGIN { >@@ -75,6 +77,13 @@ sub TransportCostMatrix { > cost => $cost, > disable_transfer => $disabled > }; >+ >+ if ( !$ignore_holds_queue_skip_closed && C4::Context->preference("HoldsQueueSkipClosed") ) { >+ $calendars->{$from} ||= Koha::DiscreteCalendar->new( branchcode => $from ); >+ $transport_cost_matrix{$to}{$from}{disable_transfer} ||= >+ $calendars->{$from}->is_holiday( $today ); >+ } >+ > } > > return \%transport_cost_matrix; >@@ -803,7 +812,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 c2bb217086..521e893b1d 100644 >--- a/C4/Overdues.pm >+++ b/C4/Overdues.pm >@@ -30,6 +30,10 @@ use Carp qw( carp ); > use C4::Context; > use C4::Accounts; > use Koha::Logger; >+use C4::Log; # logaction >+use C4::Debug; >+use Koha::DateUtils; >+use Koha::DiscreteCalendar; > use Koha::Account::Lines; > use Koha::Account::Offsets; > use Koha::Libraries; >@@ -296,7 +300,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 ); >@@ -308,7 +312,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 6e86857db8..d3345f5244 100644 >--- a/C4/Reserves.pm >+++ b/C4/Reserves.pm >@@ -33,10 +33,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 qw( dt_from_string output_pref ); >+use Koha::DiscreteCalendar; > use Koha::Hold; > use Koha::Holds; > use Koha::ItemTypes; >@@ -957,7 +957,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 5ffebb47ab..0000000000 >--- a/Koha/Calendar.pm >+++ /dev/null >@@ -1,555 +0,0 @@ >-package Koha::Calendar; >- >-use Modern::Perl; >- >-use Carp qw( croak ); >-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 addDuration { >- my ( $self, $startdate, $add_duration, $unit ) = @_; >- >- >- Koha::Exceptions::MissingParameter->throw("Missing mandatory option for Koha:Calendar->addDuration: 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->addDuration($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 addDuration >- >- my $dt = $calendar->addDuration($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/Charges/Fees.pm b/Koha/Charges/Fees.pm >index a9bbf0fe60..034e320a02 100644 >--- a/Koha/Charges/Fees.pm >+++ b/Koha/Charges/Fees.pm >@@ -21,7 +21,7 @@ use Modern::Perl; > > use Carp; > >-use Koha::Calendar; >+use Koha::DiscreteCalendar; > use Koha::DateUtils qw( dt_from_string ); > use Koha::Exceptions; > >@@ -109,7 +109,7 @@ sub accumulate_rentalcharge { > return 0 unless $rentalcharge_increment && $rentalcharge_increment > 0; > > my $duration; >- my $calendar = Koha::Calendar->new( branchcode => $self->library->id ); >+ my $calendar = Koha::DiscreteCalendar->new( branchcode => $self->library->id ); > > if ( $units eq 'hours' ) { > if ( $itemtype->rentalcharge_hourly_calendar ) { >diff --git a/Koha/DiscreteCalendar.pm b/Koha/DiscreteCalendar.pm >new file mode 100644 >index 0000000000..4875e7c189 >--- /dev/null >+++ b/Koha/DiscreteCalendar.pm >@@ -0,0 +1,1332 @@ >+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 = 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->addDuration($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'; >+ } >+ if ( ref $self->{branchcode} eq 'Koha::Library' ) { >+ $self->{branchcode} = $self->{branchcode}->branchcode; >+ } >+ $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} = undef; >+ $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) = @_; >+ >+ 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; >+ $schema->{AutoCommit} = 1; >+} >+ >+=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; >+ my $formatted_date = $dtf->format_datetime($date); >+ >+ my $rs = $schema->resultset('DiscreteCalendar')->search( >+ { >+ branchcode => $branchcode, >+ is_opened => 1, >+ }, >+ { >+ where => \['date > date(?)', $formatted_date], >+ order_by => { -asc => 'date' }, >+ rows => 1 >+ } >+ ); >+ >+ my $next = $rs->next(); >+ unless ( $next ) { >+ warn "No next opened date in calendar. Reduced to current date: $formatted_date."; >+ return $date; >+ } >+ return dt_from_string( $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; >+ my $formatted_date = $dtf->format_datetime($date); >+ >+ my $rs = $schema->resultset('DiscreteCalendar')->search( >+ { >+ branchcode => $branchcode, >+ is_opened => 1, >+ }, >+ { >+ where => \['date < date(?)', $formatted_date], >+ order_by => { -desc => 'date' }, >+ rows => 1 >+ } >+ ); >+ >+ my $prev = $rs->next(); >+ unless ( $prev ) { >+ warn "No previous opened date in calendar. Reduced to current date: $formatted_date."; >+ return $date; >+ } >+ return dt_from_string( $prev->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 addDuration >+ >+ my $dt = $calendar->addDuration($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 addDuration { >+ 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 d4fb0e4c52..0e6a05083f 100644 >--- a/Koha/Hold.pm >+++ b/Koha/Hold.pm >@@ -33,8 +33,8 @@ use Koha::Biblios; > use Koha::Items; > use Koha::Libraries; > use Koha::Old::Holds; >-use Koha::Calendar; > use Koha::Plugins; >+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 { >@@ -217,7 +217,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 ); > > $new_expiration_date = $calendar->days_forward( dt_from_string(), $max_pickup_delay ); > } >diff --git a/circ/returns.pl b/circ/returns.pl >index 6a9ec17617..3124933476 100755 >--- a/circ/returns.pl >+++ b/circ/returns.pl >@@ -45,9 +45,9 @@ use C4::Reserves qw( ModReserve ModReserveAffect GetOtherReserves ); > use C4::RotatingCollections; > use Koha::AuthorisedValues; > use Koha::BiblioFrameworks; >-use Koha::Calendar; > use Koha::Checkouts; > use Koha::DateUtils qw( dt_from_string output_pref ); >+use Koha::DiscreteCalendar; > use Koha::Holds; > use Koha::Items; > use Koha::Item::Transfers; >@@ -183,6 +183,7 @@ my $dotransfer = $query->param('dotransfer'); > my $canceltransfer = $query->param('canceltransfer'); > my $transit = $query->param('transit'); > 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..1be90a5959 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/css/discretecalendar.css >@@ -0,0 +1,213 @@ >+#jcalendar-container .ui-datepicker { >+ float: left; >+ 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; >+ float: left; >+ border: 3px solid #CCC; >+ padding: 3px; >+ margin-top: .3em; >+ margin-left: 15px; >+ background-color: #FEFEFE; >+} >+ >+fieldset.brief { >+ border: 0; >+ margin: 0; >+} >+ >+h1 select { >+ width: 20em; >+} >+ >+fieldset.brief ol { >+ font-size: 100%; >+} >+ >+fieldset.brief li, >+fieldset.brief li.radio { >+ padding: .2em 0; >+} >+ >+.CopyDatePanel { >+ margin-left: 15px; >+} >+ >+.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 0b5f33110f..1faf7a5167 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc >@@ -98,7 +98,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..ca4482bde1 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/discrete_calendar.tt >@@ -0,0 +1,708 @@ >+[% USE raw %] >+[% USE Asset %] >+[% USE Branches %] >+[% SET footerjs = 1 %] >+[% INCLUDE 'doc-head-open.inc' %] >+<title>Koha › Tools › [% Branches.GetName( branch ) | html %] calendar</title> >+[% INCLUDE 'doc-head-close.inc' %] >+[% Asset.css("css/discretecalendar.css") | $raw %] >+</head> >+ >+<body id="tools_holidays" class="tools"> >+[% INCLUDE 'header.inc' %] >+[% INCLUDE 'cat-search.inc' %] >+ >+<nav id="breadcrumbs" aria-label="Breadcrumb" class="breadcrumb"> >+ <ol> >+ <li> >+ <a href="/cgi-bin/koha/mainpage.pl">Home</a> >+ </li> >+ <li> >+ <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a> >+ </li> >+ <li> >+ <a href="#" aria-current="page"> >+ [% Branches.GetName( branch ) | html %] calendar >+ </a> >+ </li> >+ </ol> >+</nav> >+ >+<div id="main" class="main container-fluid"> >+ <div class="row"> >+ <div class="col-sm-10 col-sm-push-2"> >+ <main> >+ [% IF no_branch_selected %] >+ <div class="dialog alert"> >+ <strong>No library set!</strong> >+ </div> >+ [% END %] >+ >+ [% UNLESS datesInfos %] >+ <div class="dialog alert"> >+ <strong>Error!</strong> You have to run add_days_discrete_calendar.pl in order to use Discrete Calendar. >+ </div> >+ [% END %] >+ >+ [% IF date_format_error %] >+ <div class="dialog alert"> >+ <strong>Error!</strong> Date format error. Please try again. >+ </div> >+ [% END %] >+ >+ [% IF cannot_edit_past_dates %] >+ <div class="alert alert-danger"> >+ <strong>Error!</strong> You cannot edit the past. The date '[% error_date | html %]' was out of range for the requested action. >+ </div> >+ [% END %] >+ >+ <h2>[% Branches.GetName( branch ) | html %] calendar</h2> >+ >+ <div class="row"> >+ <div class="col-sm-8"> >+ <label for="branch">Define the holidays for:</label> >+ <form id="copyCalendar-form" method="post"> >+ <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 | html %]">[% l.branchname | html %]</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"></div> >+ >+ <!-- ***************************** Panel to deal with new holidays ********************** --> >+ <div class="panel newHoliday" id="newHoliday"> >+ <form id="newHoliday-form" method="post"> >+ <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"> >+ <strong>To date:</strong> >+ <input type="text" id="to_date_datepicker" 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"> >+ <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"> >+ <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' /> >+ </li> >+ <li> >+ <label for="closeHour">Close hours: </label><input type="text" name="closeHour" id='closeHour' /> >+ </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> >+ </li> >+ <li class="CopyDatePanel"> >+ <label>From:</label> >+ <input type="text" id="copyto_from_datepicker" size="20" class="datepicker"/> >+ <label>To:</label> >+ <input type="text" id="copyto_to_datepicker" size="20" class="datepicker"/> >+ </li> >+ </ol> >+ >+ <!-- These yyyy-mm-dd --> >+ <input type="hidden" name="from_date" id='from_date'> >+ <input type="hidden" name="to_date" id='to_date'> >+ <input type="hidden" name="copyto_from" id='copyto_from'> >+ <input type="hidden" name="copyto_to" id='copyto_to'> >+ <input type="hidden" name="daysnumber" id='daysnumber'> >+ <input type="hidden" name="local_today" id='local_today'> >+ >+ <fieldset class="action"> >+ <input type="submit" name="submit" value="Save" /> >+ <a href="#" class="cancel hidePanel newHoliday">Cancel</a> >+ </fieldset> >+ </fieldset> >+ </form> >+ </div> >+ </div> >+ >+ <div class="col-sm-4"> >+ <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="#main" onclick="go_to_date('[% need_validation_holiday.date | html %]')"><span title="[% need_validation_holiday.DATE_SORT | html %]">[% need_validation_holiday.outputdate | html %]</span></a></td> >+ <td>[% need_validation_holiday.note | html %]</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 | html %]</td> >+ <td>[% WEEK_DAYS_LOO.note | html %]</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 | html %]">[% DAY_MONTH_HOLIDAYS_LOO.day | html %]/[% DAY_MONTH_HOLIDAYS_LOO.month | html %]</span></td> >+ [% ELSE %] >+ <td><span title="[% DAY_MONTH_HOLIDAYS_LOO.DATE_SORT | html %]">[% DAY_MONTH_HOLIDAYS_LOO.month | html %]/[% DAY_MONTH_HOLIDAYS_LOO.day | html %]</span></td> >+ [% END %] >+ <td>[% DAY_MONTH_HOLIDAYS_LOO.note | html %]</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="#main" onclick="go_to_date('[% HOLIDAYS_LOO.date | html %]')"><span title="[% HOLIDAYS_LOO.DATE_SORT | html %]">[% HOLIDAYS_LOO.outputdate | html %]</span></a></td> >+ <td>[% HOLIDAYS_LOO.note | html %]</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="#main" onclick="go_to_date('[% float_holiday.date | html %]')"><span title="[% float_holiday.DATE_SORT | html %]">[% float_holiday.outputdate | html %]</span></a></td> >+ <td>[% float_holiday.note | html %]</td> >+ </tr> >+ [% END %] >+ </tbody> >+ </table> >+ [% END %] >+ </div> >+ </div> >+ </div> >+ >+ </main> >+ </div> <!-- /.col-sm-10.col-sm-push-2 --> >+ >+ <div class="col-sm-2 col-sm-pull-10"> >+ <aside> >+ [% INCLUDE 'tools-menu.inc' %] >+ </aside> >+ </div> <!-- .col-sm-2.col-sm-pull-10 --> >+ </div> <!-- /.row --> >+ >+[% MACRO jsinclude BLOCK %] >+ [% INCLUDE 'calendar.inc' %] >+ [% INCLUDE 'datatables.inc' %] >+ [% Asset.js("lib/jquery/plugins/jquery-ui-timepicker-addon.min.js") | $raw %] >+ [% Asset.js("js/tools-menu.js") | $raw %] >+ <script> >+ 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 | html %]"] = { >+ title : "[% date.note | html %]", >+ outputdate : "[% date.outputdate | html %]", >+ holiday_type:"[% date.holiday_type | html %]", >+ open_hour: "[% date.open_hour | html %]", >+ close_hour: "[% date.close_hour | html %]" >+ }; >+ [% 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").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); >+ } >+ >+ // This function gives css classes to each kind of day >+ function dateStatusHandler(date) { >+ date = getSeparetedDate(date); >+ 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 separate a given date object and 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; >+ } >+ >+ /* >+ * Validate the forms before sending them to the backend >+ */ >+ function validateForm(form) { >+ if (form =='newHoliday-form' && $('#CopyRadioButton').is(':checked')) { >+ if ($('#copyto_from_datepicker').val() =='' || $('#copyto_to_datepicker').val() =='') { >+ alert("You have to pick a FROM and TO in the Copy to different dates."); >+ return false; >+ } else if ($('#to_date_datepicker').val()) { >+ var from_DateFrom = new Date($("#jcalendar-container").datepicker("getDate")); >+ var from_DateTo = new Date($('#to_date_datepicker').datepicker("getDate")); >+ var to_DateFrom = new Date($('#copyto_from_datepicker').datepicker("getDate")); >+ var to_DateTo = new Date($('#copyto_to_datepicker').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-form') { >+ 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); >+ $('#to_date').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(); >+ $("#days_of_week").hide(); >+ $("#deleteType").hide(); >+ $(".CopyDatePanel").hide(); >+ >+ $("#branch").change(function() { >+ var branch = $(this).find("option:selected").val(); >+ location.href = '/cgi-bin/koha/tools/discrete_calendar.pl?branch=' + branch + '&calendardate=' + "[% calendardate | html %]"; >+ }); >+ >+ $("#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; >+ }); >+ >+ $("form").on("submit", function() { >+ return validateForm($(this).attr("id")); >+ }); >+ >+ // Set the correct coloring, default date and the date ranges for all datepickers >+ [% IF (dateformat == 'metric') %][% datepickerformat = 'dd/mm/yy' %] >+ [% ELSIF (dateformat == 'us' ) %][% datepickerformat = 'mm/dd/yy' %] >+ [% ELSIF (dateformat == 'iso' ) %][% datepickerformat = 'yy-mm-dd' %] >+ [% ELSIF (dateformat == 'dmydot') %][% datepickerformat = 'dd.mm.yy' %] >+ [% END %] >+ >+ $.datepicker.setDefaults({ >+ beforeShowDay: function(thedate) { >+ return dateStatusHandler(thedate); >+ }, >+ defaultDate: new Date("[% keydate | html %]"), >+ minDate: new Date("[% minDate | html %]"), >+ maxDate: new Date("[% maxDate | html %]"), >+ dateFormat: "[% datepickerformat | html %]" >+ }); >+ >+ // Main datepicker >+ $("#jcalendar-container").datepicker({ >+ onSelect: function(dateText, inst) { >+ [% IF datesInfos %] >+ var date = getSeparetedDate($(this).datepicker("getDate")); >+ var weekDay = date.weekDay; >+ var dayName = weekdays[weekDay]; >+ var dateString = date.dateString; >+ >+ // set value of form hidden field >+ $('#from_date').val(dateText); >+ >+ showHoliday(date.date_obj, dateString, dayName, date.day, date.month, date.year, weekDay, datesInfos[dateString].title, datesInfos[dateString].holiday_type); >+ [% END %] >+ }, >+ }); >+ >+ $('#to_date_datepicker').datepicker(); >+ $("#to_date_datepicker").change(function() { >+ checkRange($(this).datepicker("getDate")); >+ $('#to_date').val(($(this).val())); >+ if ($('#to_date_datepicker').val()) { >+ $('#days_of_week').show("fast"); >+ } else { >+ $('#days_of_week').hide("fast"); >+ } >+ }); >+ >+ // Datepickers for copy dates feature >+ $('#copyto_from_datepicker').datepicker(); >+ $("#copyto_from_datepicker").change(function() { >+ $('#copyto_from').val(($(this).val())); >+ }); >+ >+ $('#copyto_to_datepicker').datepicker(); >+ $("#copyto_to_datepicker").change(function() { >+ $('#copyto_to').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() { >+ $(this).closest(".panel").slideUp("fast"); >+ }); >+ >+ $("#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 ($('#to_date_datepicker').val()) { >+ $('#days_of_week').show("fast"); >+ } >+ }); >+ }); >+ </script> >+[% END %] >+ >+<!-- the main div is closed in intranet-bottom.inc --> >+[% INCLUDE 'intranet-bottom.inc' %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/holidays.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/holidays.tt >deleted file mode 100644 >index ac728fe74d..0000000000 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/holidays.tt >+++ /dev/null >@@ -1,652 +0,0 @@ >-[% USE raw %] >-[% USE Asset %] >-[% USE Branches %] >-[% SET footerjs = 1 %] >-[% INCLUDE 'doc-head-open.inc' %] >-<title>[% Branches.GetName( branch ) | html %] calendar › Tools › Koha</title> >-[% INCLUDE 'doc-head-close.inc' %] >-[% Asset.css("css/calendar.css") | $raw %] >-</head> >-<body id="tools_holidays" class="tools"> >-[% INCLUDE 'header.inc' %] >-[% INCLUDE 'cat-search.inc' %] >- >-<nav id="breadcrumbs" aria-label="Breadcrumb" class="breadcrumb"> >- <ol> >- <li> >- <a href="/cgi-bin/koha/mainpage.pl">Home</a> >- </li> >- <li> >- <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a> >- </li> >- <li> >- <a href="#" aria-current="page"> >- [% Branches.GetName( branch ) | html %] calendar >- </a> >- </li> >- </ol> >-</nav> >- >-<div class="main container-fluid"> >- <div class="row"> >- <div class="col-sm-10 col-sm-push-2"> >- <main> >- >- <h2>[% Branches.GetName( branch ) | html %] calendar</h2> >- >- <div class="row"> >- <div class="col-sm-6"> >- <label for="branch">Define the holidays for:</label> >- <select id="branch" name="branch"> >- [% PROCESS options_for_libraries libraries => Branches.all( selected => branch ) %] >- </select> >- >- <div class="panel" id="showHoliday"> >- <form action="/cgi-bin/koha/tools/exceptionHolidays.pl" method="post"> >- <input type="hidden" id="showHolidayType" name="showHolidayType" value="" /> >- <fieldset class="brief"> >- <h3>Edit this holiday</h3> >- <span id="holtype"></span> >- <ol> >- <li> >- <strong>Library:</strong> <span id="showBranchNameOutput"></span> >- <input type="hidden" id="showBranchName" name="showBranchName" /> >- </li> >- <li> >- <strong>From date:</strong> >- <span id="showDaynameOutput"></span>, >- [% IF ( dateformat == "us" ) %] >- <span id="showMonthOutput"></span>/<span id="showDayOutput"></span>/<span id="showYearOutput"></span> >- [% ELSIF ( dateformat == "metric") %] >- <span id="showDayOutput"></span>/<span id="showMonthOutput"></span>/<span id="showYearOutput"></span> >- [% ELSIF ( dateformat == "dmydot") %] >- <span id="showDayOutput"></span>.<span id="showMonthOutput"></span>.<span id="showYearOutput"></span>[% ELSE %]<span id="showYearOutput"></span>/<span id="showMonthOutput"></span>/<span id="showDayOutput"></span> >- [% END %] >- >- <input type="hidden" id="showDayname" name="showDayname" /> >- <input type="hidden" id="showWeekday" name="showWeekday" /> >- <input type="hidden" id="showDay" name="showDay" /> >- <input type="hidden" id="showMonth" name="showMonth" /> >- <input type="hidden" id="showYear" name="showYear" /> >- </li> >- <li class="dateinsert"> >- <strong>To date: </strong> >- <input type="text" id="datecancelrange" name="datecancelrange" size="20" value="[% datecancelrange | html %]" /> >- </li> >- <li> >- <label for="showTitle">Title: </label><input type="text" name="showTitle" id="showTitle" size="35" /> >- </li> >- <!-- showTitle is necessary for exception radio button to work properly --> >- <li> >- <label for="showDescription">Description:</label> >- <textarea rows="2" cols="40" id="showDescription" name="showDescription"></textarea> >- </li> >- <li class="radio"> >- <div class="exceptionPossibility" style="position:static"> >- <input type="radio" name="showOperation" id="showOperationExc" value="exception" /> <label for="showOperationExc">Generate an exception for this repeated holiday.</label> >- <a href="#" class="helptext">[?]</a> >- <div class="hint">You can make an exception for this holiday rule. This means that you will be able to say that for a repeatable holiday there is one day which is going to be an exception.</div> >- </div> >- </li> >- <li class="radio"> >- <div class="exceptionPossibility" style="position:static"> >- <input type="radio" name="showOperation" id="showOperationExcRange" value="exceptionrange" /> >- <label for="showOperationExcRange">Generate exceptions on a range of dates.</label> >- <a href="#" class="helptext">[?]</a> >- <div class="hint">You can make an exception on a range of dates repeated yearly.</div> >- </div> >- </li> >- <li class="radio"> >- <input type="radio" name="showOperation" id="showOperationDel" value="delete" /> >- <label for="showOperationDel" id="showOperationDelLabel">Delete this holiday</label> >- <a href="#" class="helptext">[?]</a> >- <div class="hint">This will delete this holiday rule. If it is a repeatable holiday, this option checks for possible exceptions. If an exception exists, this option will remove the exception and set the date to a regular holiday.</div> >- </li> >- <li class="radio"> >- <input type="radio" name="showOperation" id="showOperationDelRange" value="deleterange" /> <label for="showOperationDelRange" id="showOperationDelLabelRange">Delete the single holidays on a range</label>. >- <a href="#" class="helptext">[?]</a> >- <div class="hint">This will delete the single holidays rules only. The repeatable holidays and exceptions will not be deleted.</div> >- </li> >- <li class="radio"> >- <input type="radio" name="showOperation" id="showOperationDelRangeRepeat" value="deleterangerepeat" /> <label for="showOperationDelRangeRepeat" id="showOperationDelLabelRangeRepeat">Delete the repeated holidays on a range</label>. >- <a href="#" class="helptext">[?]</a> >- <div class="hint">This will delete the repeated holidays rules only. The repeatable holidays will be deleted but not the exceptions.</div> >- </li> >- <li class="radio"> >- <input type="radio" name="showOperation" id="showOperationDelRangeRepeatExcept" value="deleterangerepeatexcept" /> <label for="showOperationDelRangeRepeatExcept" id="showOperationDelLabelRangeRepeatExcept">Delete the exceptions on a range</label>. >- <a href="#" class="helptext">[?]</a> >- <div class="hint">This will delete the exceptions inside a given range. Be careful about your scope range; if it is oversized you could slow down Koha.</div> >- </li> >- <li class="radio"> >- <input type="radio" name="showOperation" id="showOperationEdit" value="edit" checked="checked" /> <label for="showOperationEdit">Edit this holiday</label> >- <a href="#" class="helptext">[?]</a> >- <div class="hint">This will save changes to the holiday's title and description. If the information for a repeatable holiday is modified, it affects all of the dates on which the holiday is repeated.</div></li> >- <li class="checkbox"> >- <input type="checkbox" name="allBranches" id="allBranches" /> >- <label for="allBranches">Copy changes to all libraries</label>. >- <a href="#" class="helptext">[?]</a> >- <div class="hint">If checked, changes for this holiday will be copied to all libraries. If the holiday doesn't exists for a library, no change is made.</div> >- </li> >- </ol> >- <fieldset class="action"> >- <input type="submit" name="submit" value="Save" /> >- <a href="#" class="cancel hidePanel showHoliday">Cancel</a> >- </fieldset> >- </fieldset> <!-- /.brief --> >- </form> >- </div> <!-- /#showHoliday --> >- >- <!-- Panel to deal with new holidays --> >- <div class="panel" id="newHoliday"> >- <form action="/cgi-bin/koha/tools/newHolidays.pl" method="post"> >- <fieldset class="brief"> >- <h3>Add new holiday</h3> >- <ol> >- <li> >- <strong>Library:</strong> >- <span id="newBranchNameOutput"></span> >- <input type="hidden" id="newBranchName" name="newBranchName" /> >- </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="newWeekday" name="newWeekday" /> >- <input type="hidden" id="newDay" name="newDay" /> >- <input type="hidden" id="newMonth" name="newMonth" /> >- <input type="hidden" id="newYear" name="newYear" /> >- </li> >- <li class="dateinsert"> >- <strong>To date: </strong> >- <input type="text" id="dateofrange" name="dateofrange" size="20" value="[% dateofrange | html %]" /> >- </li> >- <li> >- <label for="title">Title: </label> >- <input type="text" name="newTitle" id="title" size="35" /></li> >- <li> >- <label for="newDescription">Description:</label> >- <textarea rows="2" cols="40" id="newDescription" name="newDescription"></textarea> >- </li> >- <li class="radio"> >- <input type="radio" name="newOperation" id="newOperationOnce" value="holiday" checked="checked" /> >- <label for="newOperationOnce">Holiday only on this day</label>. >- <a href="#" class="helptext">[?]</a> >- <div class="hint">Make a single holiday. For example, selecting August 1, 2012 will make it a holiday, but will not affect August 1 in other years.</div> >- </li> >- <li class="radio"> >- <input type="radio" name="newOperation" id="newOperationDay" value="weekday" /> >- <label for="newOperationDay">Holiday repeated every same day of the week</label>. >- <a href="#" class="helptext">[?]</a> >- <div class="hint">Make this weekday a holiday, every week. For example, if your library is closed on Saturdays, use this option to make every Saturday a holiday.</div> >- </li> >- <li class="radio"> >- <input type="radio" name="newOperation" id="newOperationYear" value="repeatable" /> >- <label for="newOperationYear">Holiday repeated yearly on the same date</label>. >- <a href="#" class="helptext">[?]</a> >- <div class="hint">This will take this day and month as a reference to make it a holiday. Through this option, you can repeat this rule for every year. For example, selecting August 1 will make August 1 a holiday every year.</div> >- </li> >- <li class="radio"> >- <input type="radio" name="newOperation" id="newOperationField" value="holidayrange" /> >- <label for="newOperationField">Holidays on a range</label>. >- <a href="#" class="helptext">[?]</a> >- <div class="hint">Make a single holiday on a range. For example, selecting August 1, 2012 and August 10, 2012 will make all days between August 1 and 10 a holiday, but will not affect August 1-10 in other years.</div> >- </li> >- <li class="radio"> >- <input type="radio" name="newOperation" id="newOperationFieldyear" value="holidayrangerepeat" /> >- <label for="newOperationFieldyear">Holidays repeated yearly on a range</label>. >- <a href="#" class="helptext">[?]</a> >- <div class="hint">Make a single holiday on a range repeated yearly. For example, selecting August 1, 2012 and August 10, 2012 will make all days between August 1 and 10 a holiday, and will affect August 1-10 in other years.</div> >- </li> >- <li class="checkbox"> >- <input type="checkbox" name="allBranches" id="allBranches" /> >- <label for="allBranches">Copy to all libraries</label>. >- <a href="#" class="helptext">[?]</a> >- <div class="hint">If checked, this holiday will be copied to all libraries. If the holiday already exists for a library, no change is made.</div> >- </li> >- </ol> >- <fieldset class="action"> >- <input type="submit" name="submit" value="Save" /> >- <a href="#" class="cancel hidePanel newHoliday">Cancel</a> >- </fieldset> >- </fieldset> <!-- /.brief --> >- </form> >- </div> <!-- /#newHoliday --> >- >- <div id="calendar-container"> >- <h3>Calendar information</h3> >- <span id="calendar-anchor"></span> >- </div> >- <div style="margin-top: 2em;"> >- <form action="copy-holidays.pl" method="post"> >- <input type="hidden" name="from_branchcode" value="[% branch | html %]" /> >- <label for="branchcode">Copy holidays to:</label> >- <select id="branchcode" name="branchcode"> >- <option value=""></option> >- [% FOREACH l IN Branches.all() %] >- <option value="[% l.branchcode | html %]">[% l.branchname | html %]</option> >- [% END %] >- </select> >- <input type="submit" value="Copy" /> >- </form> >- </div> >- </div> <!-- /.col-sm-6 --> >- >- <div class="col-sm-6"> >- <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>Enter a title and description for the holiday.</li> >- <li>Specify how the holiday should repeat.</li> >- <li>Click Save to finish.</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 exception">Holiday exception</span> >- </p> >- </div> <!-- /#help --> >- >- <div id="holiday-list"> >- <!-- Exceptions First --> >- <!-- this will probably always have the least amount of data --> >- [% IF ( EXCEPTION_HOLIDAYS_LOOP ) %] >- <h3>Exceptions</h3> >- <label class="controls"> >- <input type="checkbox" name="show_past" id="show_past_holidayexceptions" class="show_past" /> >- Show past entries >- </label> >- <table id="holidayexceptions"> >- <thead> >- <tr> >- <th class="exception">Date</th> >- <th class="exception">Title</th> >- <th class="exception">Description</th> >- </tr> >- </thead> >- <tbody> >- [% FOREACH EXCEPTION_HOLIDAYS_LOO IN EXCEPTION_HOLIDAYS_LOOP %] >- <tr data-date="[% EXCEPTION_HOLIDAYS_LOO.DATE_SORT | html %]"> >- <td data-order="[% EXCEPTION_HOLIDAYS_LOO.DATE_SORT | html %]"> >- <a href="/cgi-bin/koha/tools/holidays.pl?branch=[% branch | uri %]&calendardate=[% EXCEPTION_HOLIDAYS_LOO.DATE | uri %]"> >- [% EXCEPTION_HOLIDAYS_LOO.DATE | html %] >- </a> >- </td> >- <td>[% EXCEPTION_HOLIDAYS_LOO.TITLE | html %]</td> >- <td>[% EXCEPTION_HOLIDAYS_LOO.DESCRIPTION | html %]</td> >- </tr> >- [% END %] >- </tbody> >- </table> <!-- /#holidayexceptions --> >- [% END # /IF ( EXCEPTION_HOLIDAYS_LOOP ) %] >- >- [% IF ( WEEK_DAYS_LOOP ) %] >- <h3>Weekly - Repeatable holidays</h3> >- <table id="holidayweeklyrepeatable"> >- <thead> >- <tr> >- <th class="repeatableweekly">Day of week</th> >- <th class="repeatableweekly">Title</th> >- <th class="repeatableweekly">Description</th> >- </tr> >- </thead> >- <tbody> >- [% FOREACH WEEK_DAYS_LOO IN WEEK_DAYS_LOOP %] >- <tr> >- <td>[% WEEK_DAYS_LOO.KEY | html %]</td> >- <td>[% WEEK_DAYS_LOO.TITLE | html %]</td> >- <td>[% WEEK_DAYS_LOO.DESCRIPTION | html %]</td> >- </tr> >- [% END %] >- </tbody> >- </table> <!-- /#holidayweeklyrepeatable --> >- [% END # / IF ( WEEK_DAYS_LOOP ) %] >- >- [% IF ( DAY_MONTH_HOLIDAYS_LOOP ) %] >- <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> >- <th class="repeatableyearly">Description</th> >- </tr> >- </thead> >- <tbody> >- [% FOREACH DAY_MONTH_HOLIDAYS_LOO IN DAY_MONTH_HOLIDAYS_LOOP %] >- <tr> >- <td data-order="[% DAY_MONTH_HOLIDAYS_LOO.DATE_SORT | html %]"> >- [% DAY_MONTH_HOLIDAYS_LOO.DATE | html %] >- </td> >- <td>[% DAY_MONTH_HOLIDAYS_LOO.TITLE | html %]</td> >- <td>[% DAY_MONTH_HOLIDAYS_LOO.DESCRIPTION | html %]</td> >- </tr> >- [% END %] >- </tbody> >- </table> <!-- /#holidaysyearlyrepeatable --> >- [% END # /IF ( DAY_MONTH_HOLIDAYS_LOOP ) %] >- >- [% IF ( HOLIDAYS_LOOP ) %] >- <h3>Unique holidays</h3> >- <label class="controls"> >- <input type="checkbox" name="show_past" id="show_past_holidaysunique" class="show_past" /> >- Show past entries >- </label> >- <table id="holidaysunique"> >- <thead> >- <tr> >- <th class="holiday">Date</th> >- <th class="holiday">Title</th> >- <th class="holiday">Description</th> >- </tr> >- </thead> >- <tbody> >- [% FOREACH HOLIDAYS_LOO IN HOLIDAYS_LOOP %] >- <tr data-date="[% HOLIDAYS_LOO.DATE_SORT | html %]"> >- <td data-order="[% HOLIDAYS_LOO.DATE_SORT | html %]"> >- <a href="/cgi-bin/koha/tools/holidays.pl?branch=[% branch | uri %]&calendardate=[% HOLIDAYS_LOO.DATE | uri %]"> >- [% HOLIDAYS_LOO.DATE | html %] >- </a> >- </td> >- <td>[% HOLIDAYS_LOO.TITLE | html %]</td> >- <td>[% HOLIDAYS_LOO.DESCRIPTION.replace('\\\r\\\n', '<br />') | html %]</td> >- </tr> >- [% END %] >- </tbody> >- </table> <!-- #holidaysunique --> >- [% END # /IF ( HOLIDAYS_LOOP ) %] >- </div> <!-- /#holiday-list --> >- </div> <!-- /.col-sm-6 --> >- </div> <!-- /.row --> >- </main> >- </div> <!-- /.col-sm-10.col-sm-push-2 --> >- >- <div class="col-sm-2 col-sm-pull-10"> >- <aside> >- [% INCLUDE 'tools-menu.inc' %] >- </aside> >- </div> <!-- .col-sm-2.col-sm-pull-10 --> >- </div> <!-- /.row --> >- >-[% MACRO jsinclude BLOCK %] >- [% INCLUDE 'calendar.inc' %] >- [% INCLUDE 'datatables.inc' %] >- [% Asset.js("js/tools-menu.js") | $raw %] >- <script> >- var weekdays = new Array(_("Sundays"),_("Mondays"),_("Tuesdays"),_("Wednesdays"),_("Thursdays"),_("Fridays"),_("Saturdays")); >- >- /* Creates all the structures to deal with all different kinds of holidays */ >- var week_days = new Array(); >- var holidays = new Array(); >- var holidates = new Array(); >- var exception_holidays = new Array(); >- var day_month_holidays = new Array(); >- var hola= "[% code | html %]"; >- [% FOREACH WEEK_DAYS_LOO IN WEEK_DAYS_LOOP %] >- week_days["[% WEEK_DAYS_LOO.KEY | html %]"] = {title:"[% WEEK_DAYS_LOO.TITLE | replace('"','\"') | html %]", description:"[% WEEK_DAYS_LOO.DESCRIPTION | replace('"','\"') | html %]"}; >- [% END %] >- [% FOREACH HOLIDAYS_LOO IN HOLIDAYS_LOOP %] >- holidates.push("[% HOLIDAYS_LOO.KEY | html %]"); >- holidays["[% HOLIDAYS_LOO.KEY | html %]"] = {title:"[% HOLIDAYS_LOO.TITLE | replace('"','\"') | html %]", description:"[% HOLIDAYS_LOO.DESCRIPTION | replace('"','\"') | html %]"}; >- [% END %] >- [% FOREACH EXCEPTION_HOLIDAYS_LOO IN EXCEPTION_HOLIDAYS_LOOP %] >- exception_holidays["[% EXCEPTION_HOLIDAYS_LOO.KEY | html %]"] = {title:"[% EXCEPTION_HOLIDAYS_LOO.TITLE | replace('"','\"') | html %]", description:"[% EXCEPTION_HOLIDAYS_LOO.DESCRIPTION | replace('"','\"') | html %]"}; >- [% END %] >- [% FOREACH DAY_MONTH_HOLIDAYS_LOO IN DAY_MONTH_HOLIDAYS_LOOP %] >- day_month_holidays["[% DAY_MONTH_HOLIDAYS_LOO.KEY | html %]"] = {title:"[% DAY_MONTH_HOLIDAYS_LOO.TITLE | replace('"','\"') | html %]", description:"[% DAY_MONTH_HOLIDAYS_LOO.DESCRIPTION | replace('"','\"') | html %]"}; >- [% END %] >- >- function holidayOperation(formObject, opType) { >- var op = document.getElementsByName('operation'); >- op[0].value = opType; >- formObject.submit(); >- } >- >- // This function shows the "Show Holiday" panel // >- function showHoliday (exceptionPossibility, dayName, day, month, year, weekDay, title, description, holidayType) { >- $("#newHoliday").slideUp("fast"); >- $("#showHoliday").slideDown("fast"); >- $('#showDaynameOutput').html(dayName); >- $('#showDayname').val(dayName); >- $('#showBranchNameOutput').html($("#branch :selected").text()); >- $('#showBranchName').val($("#branch").val()); >- $('#showDayOutput').html(day); >- $('#showDay').val(day); >- $('#showMonthOutput').html(month); >- $('#showMonth').val(month); >- $('#showYearOutput').html(year); >- $('#showYear').val(year); >- $('#showDescription').val(description); >- $('#showWeekday:first').val(weekDay); >- $('#showTitle').val(title); >- $('#showHolidayType').val(holidayType); >- >- if (holidayType == 'exception') { >- $("#showOperationDelLabel").html(_("Delete this exception.")); >- $("#holtype").attr("class","key exception").html(_("Holiday exception")); >- } else if(holidayType == 'weekday') { >- $("#showOperationDelLabel").html(_("Delete this holiday.")); >- $("#holtype").attr("class","key repeatableweekly").html(_("Holiday repeating weekly")); >- } else if(holidayType == 'daymonth') { >- $("#showOperationDelLabel").html(_("Delete this holiday.")); >- $("#holtype").attr("class","key repeatableyearly").html(_("Holiday repeating yearly")); >- } else { >- $("#showOperationDelLabel").html(_("Delete this holiday.")); >- $("#holtype").attr("class","key holiday").html(_("Unique holiday")); >- } >- >- if (exceptionPossibility == 1) { >- $(".exceptionPossibility").parent().show(); >- } else { >- $(".exceptionPossibility").parent().hide(); >- } >- } >- >- // This function shows the "Add Holiday" panel // >- function newHoliday (dayName, day, month, year, weekDay) { >- $("#showHoliday").slideUp("fast"); >- $("#newHoliday").slideDown("fast"); >- $("#newDaynameOutput").html(dayName); >- $("#newDayname").val(dayName); >- $("#newBranchNameOutput").html($('#branch :selected').text()); >- $("#newBranchName").val($('#branch').val()); >- $("#newDayOutput").html(day); >- $("#newDay").val(day); >- $("#newMonthOutput").html(month); >- $("#newMonth").val(month); >- $("#newYearOutput").html(year); >- $("#newYear").val(year); >- $("#newWeekday:first").val(weekDay); >- } >- >- function hidePanel(aPanelName) { >- $("#"+aPanelName).slideUp("fast"); >- } >- >- function changeBranch () { >- var branch = $("#branch option:selected").val(); >- location.href='/cgi-bin/koha/tools/holidays.pl?branch=' + branch + '&calendardate=' + "[% calendardate | html %]"; >- } >- >- /** >- * Build settings to be passed to the formatDay function for each day in the calendar >- * @param {object} dayElem - HTML node passed from Flatpickr >- * @return {void} >- */ >- function dateStatusHandler( dayElem ) { >- var day = dayElem.dateObj.getDate(); >- var month = dayElem.dateObj.getMonth() + 1; >- var year = dayElem.dateObj.getFullYear(); >- var weekDay = dayElem.dateObj.getDay(); >- var dayMonth = month + '/' + day; >- var dateString = year + '/' + month + '/' + day; >- if (exception_holidays[dateString] != null) { >- formatDay( [ "exception", _("Exception: %s").format(exception_holidays[dateString].title)], dayElem ); >- } else if ( week_days[weekDay] != null ){ >- formatDay( [ "repeatableweekly", _("Weekly holiday: %s").format(week_days[weekDay].title)], dayElem ); >- } else if ( day_month_holidays[dayMonth] != null ) { >- formatDay( [ "repeatableyearly", _("Yearly holiday: %s").format(day_month_holidays[dayMonth].title)], dayElem ); >- } else if (holidays[dateString] != null) { >- formatDay( [ "holiday", _("Single holiday: %s").format(holidays[dateString].title)], dayElem ); >- } else { >- formatDay( [ "normalday", _("Normal day")], dayElem ); >- } >- } >- >- /** >- * Adds style and title attribute to a day on the calendar >- * @param {object} settings - span class attribute ([0]) and title attribute ([1]) >- * @param {node} dayElem - HTML node passed from Flatpickr >- * @return {void} >- */ >- function formatDay( settings, dayElem ){ >- $(dayElem).attr("title", settings[1]).addClass( settings[0]); >- } >- >- /** >- * Triggers an action based on a click on a calendar day: If a holiday exists on >- * that day it loads an edit form. If there is no existing holiday one can be created >- * @param {object} calendar - a Date object corresponding to the clicked day >- * @return {void} >- */ >- function dateChanged( calendar ) { >- var day = calendar.getDate(); >- var month = calendar.getMonth() + 1; >- var year = calendar.getFullYear(); >- var weekDay = calendar.getDay(); >- var dayName = weekdays[weekDay]; >- var dayMonth = month + '/' + day; >- var dateString = year + '/' + month + '/' + day; >- if (holidays[dateString] != null) { >- showHoliday(0, dayName, day, month, year, weekDay, holidays[dateString].title, holidays[dateString].description, 'ymd'); >- } else if (exception_holidays[dateString] != null) { >- showHoliday(0, dayName, day, month, year, weekDay, exception_holidays[dateString].title, exception_holidays[dateString].description, 'exception'); >- } else if (week_days[weekDay] != null) { >- showHoliday(1, dayName, day, month, year, weekDay, week_days[weekDay].title, week_days[weekDay].description, 'weekday'); >- } else if (day_month_holidays[dayMonth] != null) { >- showHoliday(1, dayName, day, month, year, weekDay, day_month_holidays[dayMonth].title, day_month_holidays[dayMonth].description, 'daymonth'); >- } else { >- newHoliday(dayName, day, month, year, weekDay); >- } >- }; >- >- /* Custom table search configuration: If a table row >- has an "expired" class, hide it UNLESS the >- show_expired checkbox is checked */ >- $.fn.dataTable.ext.search.push( >- function( settings, searchData, index, rowData, counter ) { >- var table = settings.nTable.id; >- var row = $(settings.aoData[index].nTr); >- if( row.hasClass("date_past") && !$("#show_past_" + table ).prop("checked") ){ >- return false; >- } else { >- return true; >- } >- } >- ); >- >- // Create current date variable >- var date = new Date(); >- var datestring = date.toISOString().substring(0, 10); >- >- $(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()]); >- }); >- $("#holidayweeklyrepeatable").dataTable($.extend(true, {}, dataTablesDefaults, { >- "sDom": 't', >- "bPaginate": false >- })); >- var tables = $("#holidayexceptions, #holidaysyearlyrepeatable, #holidaysunique").DataTable($.extend(true, {}, dataTablesDefaults, { >- "sDom": 't', >- "bPaginate": false, >- "createdRow": function( row, data, dataIndex ) { >- var holiday = $(row).data("date"); >- if( holiday < datestring ){ >- $(row).addClass("date_past"); >- } >- } >- })); >- >- $(".show_past").on("change", function(){ >- tables.draw(); >- }); >- >- $("a.helptext").click(function(){ >- $(this).parent().find(".hint").toggle(); return false; >- }); >- >- var dateofrange = $("#dateofrange").flatpickr(); >- >- var datecancelrange = $("#datecancelrange").flatpickr(); >- >- $("#dateofrange").each(function () { this.value = "" }); >- $("#datecancelrange").each(function () { this.value = "" }); >- >- var maincalendar = $("#calendar-anchor").flatpickr({ >- inline: true, >- onReady: function(){ >- return; >- }, >- onDayCreate: function( dObj, dStr, fp, dayElem ){ >- /* for each day on the calendar, get the >- correct status information for the date */ >- dateStatusHandler( dayElem ); >- }, >- onChange: function( selectedDates, dateStr, instance ){ >- var fromdate = selectedDates[0]; >- var enddate = dateofrange.selectedDates[0]; >- >- dateChanged( fromdate ); >- >- dateofrange.set( 'defaultDate', fromdate ); >- dateofrange.set( 'minDate', fromdate ); >- >- if ( enddate != undefined ) { >- if ( enddate < fromdate ) { >- dateofrange.set("defaultDate", fromdate); >- dateofrange.setDate(fromdate); >- } >- } >- >- }, >- defaultDate: new Date("[% keydate | html %]") >- }); >- >- $(".hidePanel").on("click",function(){ >- if( $(this).hasClass("showHoliday") ){ >- hidePanel("showHoliday"); >- } else { >- hidePanel("newHoliday"); >- } >- }) >- }); >- </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 5d0d7f84cb..58e31bfd7c 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 >@@ -100,7 +100,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..327c35c315 >--- /dev/null >+++ b/misc/cronjobs/add_days_discrete_calendar.pl >@@ -0,0 +1,165 @@ >+#!/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; >+use Koha::DateUtils; >+ >+# Options >+my $help = 0; >+my $daysInFuture = 1; >+my $branch = undef; >+my $debug = 0; >+GetOptions ( >+ 'help|?|h' => \$help, >+ 'n=i' => \$daysInFuture, >+ 'b|branch=s' => \$branch, >+ '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 >+ -b --branch: branchcode of the library to which days will be added >+ -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 >+my @branches = (); >+if ( $branch ) { >+ push @branches, $branch; >+} else { >+ $query = 'SELECT branchcode FROM discrete_calendar GROUP BY branchcode'; >+ $statement = $dbh->prepare($query); >+ $statement->execute(); >+ for my $branchcode ( @{$statement->fetchall_arrayref} ) { >+ push @branches, $branchcode->[0]; >+ } >+} >+ >+foreach my $branchCode (@branches) { >+ #get the latest date in the table >+ $query = "SELECT MAX(date) FROM discrete_calendar WHERE branchcode = ?"; >+ $statement = $dbh->prepare($query); >+ $statement->execute($branchCode); >+ my $latestDate = $statement->fetchrow_array; >+ >+ if ( $latestDate ) { >+ my $parser = DateTime::Format::Strptime->new( >+ pattern => '%Y-%m-%d %H:%M:%S', >+ on_error => 'croak', >+ ); >+ $latestDate = $parser->parse_datetime($latestDate); >+ } else { >+ $latestDate = dt_from_string(); >+ } >+ >+ my $newDay = $latestDate->clone(); >+ $latestDate->add(days => $daysInFuture); >+ >+ for ($newDay->add(days => 1); $newDay <= $latestDate; $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, holiday_type, note 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 (?, ?, ?, ?, ?)'; >+ >+ #insert into discrete_calendar >+ $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, $weekly_holiday_type, $weekly_note ) = $statement->fetchrow_array; >+ >+ # weekly repeatable holidays >+ if ( $weekly_holiday_type && $weekly_holiday_type eq 'W' ) { >+ $is_opened = 0; >+ $holiday_type = $weekly_holiday_type unless $holiday_type; >+ $note = $weekly_note unless $note; >+ } >+ >+ my $data = { >+ date => output_pref( { dt => $newDay, dateformat => 'iso', timeformat => '24hr' }), >+ branchcode => $branchCode, >+ }; >+ >+ $data->{is_opened} = $is_opened if ( defined $is_opened ); >+ $data->{holiday_type} = $holiday_type if ( defined $holiday_type ); >+ $data->{note} = $note if ( defined $note ); >+ $data->{open_hour} = $open_hour // "09:00:00"; >+ $data->{close_hour} = $close_hour // "17:00:00"; >+ >+ my $calendar_date = $schema->resultset( "DiscreteCalendar" )->create( $data )->get_from_storage(); >+ >+ if ( $debug && !$@ ) { >+ warn "Added day " . $calendar_date->date >+ . " to " . $calendar_date->branchcode >+ . " is opened: " . $calendar_date->is_opened >+ . ", holiday_type: " . $calendar_date->holiday_type >+ . ", note: " . $calendar_date->note >+ . ", open_hour: " . $calendar_date->open_hour >+ . ", close_hour: " . $calendar_date->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 3edf6ed736..2ede371105 100755 >--- a/misc/cronjobs/fines.pl >+++ b/misc/cronjobs/fines.pl >@@ -38,8 +38,8 @@ use Carp qw( carp croak ); > use File::Spec; > use Try::Tiny qw( catch try ); > >-use Koha::Calendar; > use Koha::DateUtils qw( dt_from_string output_pref ); >+use Koha::DiscreteCalendar; > use Koha::Patrons; > use C4::Log qw( cronlogaction ); > >@@ -203,7 +203,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 ea5d70ab48..09e7879376 100755 >--- a/misc/cronjobs/holds/cancel_unfilled_holds.pl >+++ b/misc/cronjobs/holds/cancel_unfilled_holds.pl >@@ -25,7 +25,8 @@ use Koha::Script -cron; > use C4::Reserves; > use C4::Log qw( cronlogaction ); > use Koha::Holds; >-use Koha::Calendar; >+use Koha::DiscreteCalendar; >+use Koha::DateUtils; > use Koha::Libraries; > > cronlogaction(); >diff --git a/misc/cronjobs/overdue_notices.pl b/misc/cronjobs/overdue_notices.pl >index 9fdafe13b4..d63ff79ca3 100755 >--- a/misc/cronjobs/overdue_notices.pl >+++ b/misc/cronjobs/overdue_notices.pl >@@ -33,7 +33,7 @@ use C4::Overdues qw( GetOverdueMessageTransportTypes parse_overdues_letter ); > use C4::Log qw( cronlogaction ); > use Koha::Patron::Debarments qw( AddUniqueDebarment ); > use Koha::DateUtils qw( dt_from_string output_pref ); >-use Koha::Calendar; >+use Koha::DiscreteCalendar; > use Koha::Libraries; > use Koha::Acquisition::Currencies; > use Koha::Patrons; >@@ -452,9 +452,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; > } >@@ -566,13 +565,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) { >@@ -656,9 +653,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 d4141b25f3..77e7741eb2 100755 >--- a/misc/cronjobs/staticfines.pl >+++ b/misc/cronjobs/staticfines.pl >@@ -32,11 +32,14 @@ use Date::Calc qw( Date_to_Days ); > use Koha::Script -cron; > use C4::Context; > use C4::Overdues qw( CalcFine checkoverdues GetFine Getoverdues ); >-use C4::Calendar qw(); # don't need any exports from Calendar > use C4::Log qw( cronlogaction ); > use Getopt::Long qw( GetOptions ); > use List::MoreUtils qw( none ); > use Koha::DateUtils qw( dt_from_string output_pref ); >+use C4::Circulation; >+use Koha::DiscreteCalendar qw(); # don't need any exports from Calendar >+use C4::Biblio; >+use C4::Debug; # supplying $debug and $cgi_debug > use Koha::Patrons; > > my $help = 0; >@@ -166,7 +169,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 6a1456987f..94b3b62f54 100755 >--- a/misc/cronjobs/thirdparty/TalkingTech_itiva_outbound.pl >+++ b/misc/cronjobs/thirdparty/TalkingTech_itiva_outbound.pl >@@ -27,8 +27,8 @@ use Koha::Script -cron; > use C4::Context; > use C4::Letters; > use C4::Overdues; >-use Koha::Calendar; > use Koha::DateUtils qw( dt_from_string output_pref ); >+use Koha::DiscreteCalendar; > use Koha::Patrons; > use Koha::Libraries; > >@@ -335,7 +335,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 381f9e70a5..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 qw( checkauth ); >-use C4::Output; >- >- >-use C4::Calendar; >- >-my $input = CGI->new; >-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..1adf19b72e >--- /dev/null >+++ b/tools/discrete_calendar.pl >@@ -0,0 +1,175 @@ >+#!/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 = CGI->new; >+ >+# Get the template to use >+my ($template, $loggedinuser, $cookie) = get_template_and_user( >+ { >+ template_name => "tools/discrete_calendar.tt", >+ type => "intranet", >+ query => $input, >+ 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_date') ||''; >+ my $from_endDate = $input->param('to_date') || ''; >+ my $to_startDate = $input->param('copyto_from') || ''; >+ my $to_endDate = $input->param('copyto_to') || ''; >+ my $local_today = $input->param('local_today'); >+ my $daysnumber = $input->param('daysnumber'); >+ >+ $from_startDate = eval { dt_from_string(scalar $from_startDate) } if $from_startDate ne ''; >+ $from_endDate = eval { dt_from_string(scalar $from_endDate) } if $from_endDate ne ''; >+ $to_startDate = eval { dt_from_string(scalar $to_startDate) } if $to_startDate ne ''; >+ $to_endDate = eval { dt_from_string(scalar $to_endDate) } if $to_endDate ne ''; >+ $local_today = eval { dt_from_string( $local_today, 'iso') }; >+ >+ unless ($@) { >+ my $diff_from_today = $local_today - $to_startDate; >+ if ( $diff_from_today->is_positive ) { >+ $template->param( >+ cannot_edit_past_dates => 1, >+ error_date => $to_startDate >+ ); >+ } >+ else { >+ $calendar->copy_holiday($from_startDate, $from_endDate, $to_startDate, $to_endDate, $daysnumber); >+ } >+ } else { >+ $template->param( date_format_error => 1 ); >+ } >+} elsif ($action eq 'edit') { >+ my $openHour = $input->param('openHour'); >+ my $closeHour = $input->param('closeHour'); >+ my $startDate = $input->param('from_date'); >+ my $endDate = $input->param('to_date'); >+ my $deleteType = $input->param('deleteType') || 0; >+ #Get today from javascript for a precise local time >+ my $local_today = $input->param('local_today'); >+ $local_today = eval { dt_from_string( $local_today, 'iso') }; >+ >+ $startDate = eval { dt_from_string(scalar $startDate) }; >+ >+ unless ($@) { >+ if($endDate ne '' ) { >+ $endDate = eval { dt_from_string(scalar $endDate) }; >+ } else { >+ $endDate = $startDate->clone(); >+ } >+ >+ $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 >+ }); >+ } else { >+ $template->param( date_format_error => 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; >+ >+# 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(); >+ >+#Calendar minimum date >+my $maxDate = $calendar->get_max_date(); >+ >+my @datesInfos = $calendar->get_dates_info(); >+ >+$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 a96b1d0ebb..0000000000 >--- a/tools/exceptionHolidays.pl >+++ /dev/null >@@ -1,143 +0,0 @@ >-#!/usr/bin/perl >- >-use Modern::Perl; >- >-use CGI qw ( -utf8 ); >- >-use C4::Auth qw( checkauth ); >-use C4::Output; >-use DateTime; >- >-use C4::Calendar; >-use Koha::DateUtils qw( dt_from_string ); >- >-my $input = CGI->new; >-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 ddf45db1dc..0000000000 >--- a/tools/holidays.pl >+++ /dev/null >@@ -1,130 +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 qw( get_template_and_user ); >-use C4::Output qw( output_html_with_http_headers ); >- >-use C4::Calendar; >-use Koha::DateUtils qw( dt_from_string output_pref ); >- >-my $input = CGI->new; >- >-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'}, >- }); >- >-# 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 b07079d52b..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 qw( checkauth ); >-use C4::Output; >- >-use C4::Calendar; >-use DateTime; >-use Koha::DateUtils qw( dt_from_string output_pref ); >- >-my $input = CGI->new; >-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.30.2
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 17015
:
53859
|
54318
|
54419
|
59166
|
59167
|
59168
|
59169
|
59170
|
59171
|
59267
|
59268
|
59269
|
59270
|
59271
|
59463
|
59516
|
59517
|
59518
|
59519
|
59520
|
59561
|
59562
|
59586
|
59587
|
59888
|
59889
|
60902
|
60903
|
60904
|
60905
|
60906
|
60986
|
61737
|
62375
|
62376
|
62380
|
63257
|
63282
|
63362
|
63363
|
63364
|
63365
|
63366
|
63367
|
64235
|
65073
|
65074
|
65075
|
65076
|
65077
|
67721
|
67722
|
67723
|
67724
|
67725
|
67879
|
67929
|
67930
|
67931
|
67932
|
67933
|
67934
|
68392
|
68393
|
68394
|
68395
|
68396
|
68397
|
71634
|
71635
|
71636
|
71637
|
71638
|
72890
|
73145
|
74859
|
74860
|
74861
|
74862
|
74863
|
74864
|
74865
|
74866
|
75444
|
75479
|
76594
|
77249
|
77250
|
77607
|
77608
|
77609
|
77610
|
77611
|
77612
|
77613
|
77770
|
77771
|
77772
|
77773
|
77774
|
79035
|
80523
|
80524
|
80525
|
80526
|
80527
|
80528
|
80529
|
80530
|
80531
|
80532
|
80533
|
80534
|
80535
|
83547
|
85394
|
85677
|
85678
|
85679
|
85680
|
85681
|
85682
|
85683
|
85684
|
85685
|
85686
|
85687
|
85688
|
85689
|
85690
|
85691
|
92595
|
92596
|
92597
|
92598
|
100079
|
110383
|
110384
|
110386
|
110387
|
110388
|
110389
|
113541
|
113905
|
115501
|
115502
|
115503
|
115504
|
115505
|
115506
|
115507
|
115508
|
115509
|
115510
|
115511
|
118554
|
118555
|
118556
|
118557
|
118558
|
118559
|
119095
|
119097
|
119099
|
119100
|
119101
|
119102
|
131619
|
131620
|
131621
|
131622
|
131623
|
131624
|
131625
|
131626
|
131634
|
131635
|
131636
|
131637
|
131638
|
131639
|
131640
|
131641
|
131667
|
132199
|
133596
|
133597
|
133598
|
133599
|
133600
|
133601
|
133602
|
133603
|
133604
|
133605
|
133678
|
137219
|
137220
|
137221
|
137222
|
137223
|
137224
|
137225
|
137226
|
137227
|
137228
|
137229
|
139378
|
139379
|
139380
|
139381
|
139382
|
139599
|
139600
|
139601
|
139602
|
139603
|
139851
|
140150
|
141176
|
144257
|
144258
|
144259
|
144260
|
144261
|
144262
|
144264
|
144268
|
144269
|
144270
|
144271
|
144272
|
144273
|
151438
|
151439
|
151440
|
151441
|
151442
|
151443
|
151444
|
151445
|
151446
|
151447
|
151448
|
151449
|
151450
|
156340
|
156341
|
156342
|
156343
|
156344
|
156345
|
156346
|
156347
|
156348
|
156349
|
156350
|
156351
|
156352
|
156353
|
156354
|
156355
|
157656
|
157657
|
157658
|
157659
|
157660
|
157661
|
157662
|
157663
|
157664
|
157665
|
157666
|
157667
|
157668
|
157669
|
157670
|
157671
|
157672
|
167805
|
167806
|
167807
|
167808
|
167809