commit for holidays and news management.
[koha.git] / C4 / Calendar.pm
1 package C4::Calendar;\r
2 \r
3 # This file is part of Koha.\r
4 #\r
5 # Koha is free software; you can redistribute it and/or modify it under the\r
6 # terms of the GNU General Public License as published by the Free Software\r
7 # Foundation; either version 2 of the License, or (at your option) any later\r
8 # version.\r
9 #\r
10 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY\r
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r
12 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.\r
13 #\r
14 # You should have received a copy of the GNU General Public License along with\r
15 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r
16 # Suite 330, Boston, MA  02111-1307 USA\r
17 \r
18 use strict;\r
19 require Exporter;\r
20 use vars qw($VERSION @EXPORT);\r
21 \r
22 use C4::Database;\r
23 use Date::Manip;\r
24 # use Date::Calc;\r
25 \r
26 # set the version for version checking\r
27 $VERSION = 0.01;\r
28 \r
29 =head1 NAME\r
30 \r
31 C4::Calendar::Calendar - Koha module dealing with holidays.\r
32 \r
33 =head1 SYNOPSIS\r
34 \r
35         use C4::Calendar::Calendar;\r
36 \r
37 =head1 DESCRIPTION\r
38 \r
39 This package is used to deal with holidays. Through this package, you can set all kind of holidays for the library.\r
40 \r
41 =head1 FUNCTIONS\r
42 \r
43 =over 2\r
44 \r
45 =cut\r
46 \r
47 @EXPORT = qw(&new \r
48              &change_branchcode \r
49                          &get_week_days_holidays \r
50                          &get_day_month_holidays \r
51              &get_exception_holidays \r
52                          &get_single_holidays \r
53                          &insert_week_day_holiday \r
54                          &insert_day_month_holiday \r
55                          &insert_single_holiday \r
56                          &insert_exception_holiday\r
57                          &delete_holiday \r
58                          &isHoliday \r
59                          &addDate\r
60                          &daysBetween);\r
61 \r
62 =item new\r
63 \r
64         $calendar = C4::Calendar::Calendar->new(branchcode => $branchcode);\r
65 \r
66 C<$branchcode> Is the branch code wich you want to use calendar.\r
67 \r
68 =cut\r
69 \r
70 sub new {\r
71         my $classname = shift @_;\r
72         my %options = @_;\r
73 \r
74         my %hash;\r
75         my $self = bless(\%hash, $classname);\r
76 \r
77         foreach my $optionName (keys %options) {\r
78                 $self->{lc($optionName)} = $options{$optionName};\r
79         }\r
80 \r
81         $self->_init;\r
82 \r
83         return $self;\r
84 }\r
85 \r
86 sub _init {\r
87         my $self = shift @_;\r
88 \r
89         my $dbh = C4::Context->dbh();\r
90         my $week_days_sql = $dbh->prepare("select weekday, title, description from repeatable_holidays where ('$self->{branchcode}' = branchcode) and (NOT(ISNULL(weekday)))");\r
91         $week_days_sql->execute;\r
92         my %week_days_holidays;\r
93         while (my ($weekday, $title, $description) = $week_days_sql->fetchrow) {\r
94                 $week_days_holidays{$weekday}{title} = $title;\r
95                 $week_days_holidays{$weekday}{description} = $description;\r
96         }\r
97         $week_days_sql->finish;\r
98         $self->{'week_days_holidays'} = \%week_days_holidays;\r
99 \r
100         my $day_month_sql = $dbh->prepare("select day, month, title, description from repeatable_holidays where ('$self->{branchcode}' = branchcode) and ISNULL(weekday)");\r
101         $day_month_sql->execute;\r
102         my %day_month_holidays;\r
103         while (my ($day, $month, $title, $description) = $day_month_sql->fetchrow) {\r
104                 $day_month_holidays{"$month/$day"}{title} = $title;\r
105                 $day_month_holidays{"$month/$day"}{description} = $description;\r
106         }\r
107         $day_month_sql->finish;\r
108         $self->{'day_month_holidays'} = \%day_month_holidays;\r
109 \r
110         my $exception_holidays_sql = $dbh->prepare("select day, month, year, title, description from special_holidays where ('$self->{branchcode}' = branchcode) and (isexception = 1)");\r
111         $exception_holidays_sql->execute;\r
112         my %exception_holidays;\r
113         while (my ($day, $month, $year, $title, $description) = $exception_holidays_sql->fetchrow) {\r
114                 $exception_holidays{"$year/$month/$day"}{title} = $title;\r
115                 $exception_holidays{"$year/$month/$day"}{description} = $description;\r
116         }\r
117         $exception_holidays_sql->finish;\r
118         $self->{'exception_holidays'} = \%exception_holidays;\r
119 \r
120         my $holidays_sql = $dbh->prepare("select day, month, year, title, description from special_holidays where ('$self->{branchcode}' = branchcode) and (isexception = 0)");\r
121         $holidays_sql->execute;\r
122         my %single_holidays;\r
123         while (my ($day, $month, $year, $title, $description) = $holidays_sql->fetchrow) {\r
124                 $single_holidays{"$year/$month/$day"}{title} = $title;\r
125                 $single_holidays{"$year/$month/$day"}{description} = $description;\r
126         }\r
127         $holidays_sql->finish;\r
128         $self->{'single_holidays'} = \%single_holidays;\r
129 }\r
130 \r
131 =item change_branchcode\r
132 \r
133         $calendar->change_branchcode(branchcode => $branchcode)\r
134 \r
135 Change the calendar branch code. This means to change the holidays structure.\r
136 \r
137 C<$branchcode> Is the branch code wich you want to use calendar.\r
138 \r
139 =cut\r
140 \r
141 sub change_branchcode {\r
142         my ($self, $branchcode) = @_;\r
143         my %options = @_;\r
144 \r
145         foreach my $optionName (keys %options) {\r
146                 $self->{lc($optionName)} = $options{$optionName};\r
147         }\r
148         $self->_init;\r
149 \r
150         return $self;\r
151 }\r
152 \r
153 =item get_week_days_holidays\r
154 \r
155         $week_days_holidays = $calendar->get_week_days_holidays();\r
156 \r
157 Returns a hash reference to week days holidays.\r
158 \r
159 =cut\r
160 \r
161 sub get_week_days_holidays {\r
162         my $self = shift @_;\r
163         my $week_days_holidays = $self->{'week_days_holidays'};\r
164         return $week_days_holidays;\r
165 }\r
166 \r
167 =item get_day_month_holidays\r
168         \r
169         $day_month_holidays = $calendar->get_day_month_holidays();\r
170 \r
171 Returns a hash reference to day month holidays.\r
172 \r
173 =cut\r
174 \r
175 sub get_day_month_holidays {\r
176         my $self = shift @_;\r
177         my $day_month_holidays = $self->{'day_month_holidays'};\r
178         return $day_month_holidays;\r
179 }\r
180 \r
181 =item get_exception_holidays\r
182         \r
183         $exception_holidays = $calendar->exception_holidays();\r
184 \r
185 Returns a hash reference to exception holidays. This kind of days are those\r
186 which stands for a holiday, but you wanted to make an exception for this particular\r
187 date.\r
188 \r
189 =cut\r
190 \r
191 sub get_exception_holidays {\r
192         my $self = shift @_;\r
193         my $exception_holidays = $self->{'exception_holidays'};\r
194         return $exception_holidays;\r
195 }\r
196 \r
197 =item get_single_holidays\r
198         \r
199         $single_holidays = $calendar->get_single_holidays();\r
200 \r
201 Returns a hash reference to single holidays. This kind of holidays are those which\r
202 happend just one time.\r
203 \r
204 =cut\r
205 \r
206 sub get_single_holidays {\r
207         my $self = shift @_;\r
208         my $single_holidays = $self->{'single_holidays'};\r
209         return $single_holidays;\r
210 }\r
211 \r
212 =item insert_week_day_holiday\r
213 \r
214         insert_week_day_holiday(weekday => $weekday,\r
215                                                         title => $title,\r
216                                                         description => $description);\r
217 \r
218 Inserts a new week day for $self->{branchcode}.\r
219 \r
220 C<$day> Is the week day to make holiday.\r
221 \r
222 C<$title> Is the title to store for the holiday formed by $year/$month/$day.\r
223 \r
224 C<$description> Is the description to store for the holiday formed by $year/$month/$day.\r
225 \r
226 =cut\r
227 \r
228 sub insert_week_day_holiday {\r
229         my $self = shift @_;\r
230         my %options = @_;\r
231 \r
232         my $dbh = C4::Context->dbh();\r
233         my $insertHoliday = $dbh->prepare("insert into repeatable_holidays (id,branchcode,weekday,day,month,title,description) values ('', '$self->{branchcode}', $options{weekday}, NULL, NULL, '$options{title}', '$options{description}')");\r
234         $insertHoliday->execute;\r
235         $insertHoliday->finish;\r
236 \r
237         $self->{'week_days_holidays'}->{$options{weekday}}{title} = $options{title};\r
238         $self->{'week_days_holidays'}->{$options{weekday}}{description} = $options{description};\r
239         return $self;\r
240 }\r
241 \r
242 =item insert_day_month_holiday\r
243 \r
244         insert_day_month_holiday(day => $day,\r
245                                  month => $month,\r
246                                                          title => $title,\r
247                                                          description => $description);\r
248 \r
249 Inserts a new day month holiday for $self->{branchcode}.\r
250 \r
251 C<$day> Is the day month to make the date to insert.\r
252 \r
253 C<$month> Is month to make the date to insert.\r
254 \r
255 C<$title> Is the title to store for the holiday formed by $year/$month/$day.\r
256 \r
257 C<$description> Is the description to store for the holiday formed by $year/$month/$day.\r
258 \r
259 =cut\r
260 \r
261 sub insert_day_month_holiday {\r
262         my $self = shift @_;\r
263         my %options = @_;\r
264 \r
265         my $dbh = C4::Context->dbh();\r
266         my $insertHoliday = $dbh->prepare("insert into repeatable_holidays (id,branchcode,weekday,day,month,title,description) values ('', '$self->{branchcode}', NULL, $options{day}, $options{month}, '$options{title}', '$options{description}')");\r
267         $insertHoliday->execute;\r
268         $insertHoliday->finish;\r
269 \r
270         $self->{'day_month_holidays'}->{"$options{month}/$options{day}"}{title} = $options{title};\r
271         $self->{'day_month_holidays'}->{"$options{month}/$options{day}"}{description} = $options{description};\r
272         return $self;\r
273 }\r
274 \r
275 =item insert_single_holiday\r
276 \r
277         insert_single_holiday(day => $day,\r
278                               month => $month,\r
279                                                   year => $year,\r
280                                                   title => $title,\r
281                                                   description => $description);\r
282 \r
283 Inserts a new single holiday for $self->{branchcode}.\r
284 \r
285 C<$day> Is the day month to make the date to insert.\r
286 \r
287 C<$month> Is month to make the date to insert.\r
288 \r
289 C<$year> Is year to make the date to insert.\r
290 \r
291 C<$title> Is the title to store for the holiday formed by $year/$month/$day.\r
292 \r
293 C<$description> Is the description to store for the holiday formed by $year/$month/$day.\r
294 \r
295 =cut\r
296 \r
297 sub insert_single_holiday {\r
298         my $self = shift @_;\r
299         my %options = @_;\r
300 \r
301         my $dbh = C4::Context->dbh();\r
302         my $isexception = 0;\r
303         my $insertHoliday = $dbh->prepare("insert into special_holidays (id,branchcode,day,month,year,isexception,title,description) values ('', '$self->{branchcode}', $options{day}, $options{month}, $options{year}, $isexception, '$options{title}', '$options{description}')");\r
304         $insertHoliday->execute;\r
305         $insertHoliday->finish;\r
306 \r
307         $self->{'single_holidays'}->{"$options{year}/$options{month}/$options{day}"}{title} = $options{title};\r
308         $self->{'single_holidays'}->{"$options{year}/$options{month}/$options{day}"}{description} = $options{description};\r
309         return $self;\r
310 }\r
311 \r
312 =item insert_exception_holiday\r
313 \r
314         insert_exception_holiday(day => $day,\r
315                                  month => $month,\r
316                                                      year => $year,\r
317                                                      title => $title,\r
318                                                      description => $description);\r
319 \r
320 Inserts a new exception holiday for $self->{branchcode}.\r
321 \r
322 C<$day> Is the day month to make the date to insert.\r
323 \r
324 C<$month> Is month to make the date to insert.\r
325 \r
326 C<$year> Is year to make the date to insert.\r
327 \r
328 C<$title> Is the title to store for the holiday formed by $year/$month/$day.\r
329 \r
330 C<$description> Is the description to store for the holiday formed by $year/$month/$day.\r
331 \r
332 =cut\r
333 \r
334 sub insert_exception_holiday {\r
335         my $self = shift @_;\r
336         my %options = @_;\r
337 \r
338         my $dbh = C4::Context->dbh();\r
339         my $isexception = 1;\r
340         my $insertException = $dbh->prepare("insert into special_holidays (id,branchcode,day,month,year,isexception,title,description) values ('', '$self->{branchcode}', $options{day}, $options{month}, $options{year}, $isexception, '$options{title}', '$options{description}')");\r
341         $insertException->execute;\r
342         $insertException->finish;\r
343 \r
344         $self->{'exceptions_holidays'}->{"$options{year}/$options{month}/$options{day}"}{title} = $options{title};\r
345         $self->{'exceptions_holidays'}->{"$options{year}/$options{month}/$options{day}"}{description} = $options{description};\r
346         return $self;\r
347 }\r
348 \r
349 =item delete_holiday\r
350 \r
351         delete_holiday(weekday => $weekday\r
352                        day => $day,\r
353                        month => $month,\r
354                                    year => $year);\r
355 \r
356 Delete a holiday for $self->{branchcode}.\r
357 \r
358 C<$weekday> Is the week day to delete.\r
359 \r
360 C<$day> Is the day month to make the date to delete.\r
361 \r
362 C<$month> Is month to make the date to delete.\r
363 \r
364 C<$year> Is year to make the date to delete.\r
365 \r
366 =cut\r
367 \r
368 sub delete_holiday {\r
369         my $self = shift @_;\r
370         my %options = @_;\r
371 \r
372         # Verify what kind of holiday that day is. For example, if it is\r
373         # a repeatable holiday, this should check if there are some exception\r
374         # for that holiday rule. Otherwise, if it is a regular holiday, it´s \r
375         # ok just deleting it.\r
376 \r
377         my $dbh = C4::Context->dbh();\r
378         my $isSingleHoliday = $dbh->prepare("select id from special_holidays where (branchcode = '$self->{branchcode}') and (day = $options{day}) and (month = $options{month}) and (year = $options{year})");\r
379         $isSingleHoliday->execute;\r
380         if ($isSingleHoliday->rows) {\r
381                 my $id = $isSingleHoliday->fetchrow;\r
382                 $isSingleHoliday->finish; # Close the last query\r
383 \r
384                 my $deleteHoliday = $dbh->prepare("delete from special_holidays where (id = $id)");\r
385                 $deleteHoliday->execute;\r
386                 $deleteHoliday->finish; # Close the last query\r
387                 delete($self->{'single_holidays'}->{"$options{year}/$options{month}/$options{day}"});\r
388         } else {        \r
389                 $isSingleHoliday->finish; # Close the last query\r
390 \r
391                 my $isWeekdayHoliday = $dbh->prepare("select id from repeatable_holidays where (branchcode = '$self->{branchcode}') and (weekday = $options{weekday})");\r
392                 $isWeekdayHoliday->execute;\r
393                 if ($isWeekdayHoliday->rows) {\r
394                         my $id = $isWeekdayHoliday->fetchrow;\r
395                         $isWeekdayHoliday->finish; # Close the last query\r
396 \r
397                         my $updateExceptions = $dbh->prepare("update special_holidays set isexception = 0 where (WEEKDAY(CONCAT(special_holidays.year,'-',special_holidays.month,'-',special_holidays.day)) = $options{weekday}) and (branchcode = '$self->{branchcode}')");\r
398                         $updateExceptions->execute;\r
399                         $updateExceptions->finish; # Close the last query\r
400 \r
401                         my $deleteHoliday = $dbh->prepare("delete from repeatable_holidays where (id = $id)");\r
402                         $deleteHoliday->execute;\r
403                         $deleteHoliday->finish;\r
404                         delete($self->{'week_days_holidays'}->{$options{weekday}});\r
405                 } else {\r
406                         $isWeekdayHoliday->finish; # Close the last query\r
407 \r
408                         my $isDayMonthHoliday = $dbh->prepare("select id from repeatable_holidays where (branchcode = '$self->{branchcode}') (day = $options{day}) and (month = $options{month})");\r
409                         $isDayMonthHoliday->execute;\r
410                         if ($isDayMonthHoliday->rows) {\r
411                                 my $id = $isDayMonthHoliday->fetchrow;\r
412                                 $isDayMonthHoliday->finish;\r
413                                 my $updateExceptions = $dbh->prepare("update special_holidays set isexception = 0 where (special_holidays.branchcode = '$self->{branchcode}') and (special_holidays.day = $options{day}) and (special_holidays.month = $options{month})");\r
414                                 $updateExceptions->execute;\r
415                                 $updateExceptions->finish; # Close the last query\r
416 \r
417                                 my $deleteHoliday = $dbh->prepare("delete from repeatable_holidays where (id = $id)");\r
418                                 $deleteHoliday->execute;\r
419                                 $deleteHoliday->finish; # Close the last query\r
420                                 $isDayMonthHoliday->finish; # Close the last query\r
421                                 delete($self->{'day_month_holidays'}->{"$options{month}/$options{day}"});\r
422                         }\r
423                 }\r
424         }       \r
425         return $self;\r
426 }\r
427 \r
428 =item isHoliday\r
429         \r
430         $isHoliday = isHoliday($day, $month $year);\r
431 \r
432 \r
433 C<$day> Is the day to check wether if is a holiday or not.\r
434 \r
435 C<$month> Is the month to check wether if is a holiday or not.\r
436 \r
437 C<$year> Is the year to check wether if is a holiday or not.\r
438 \r
439 =cut\r
440 \r
441 sub isHoliday {\r
442         my ($self, $day, $month, $year) = @_;\r
443 \r
444         my $weekday = Date_DayOfWeek($month, $day, $year) % 7;\r
445         \r
446         my $weekDays = $self->get_week_days_holidays();\r
447         my $dayMonths = $self->get_day_month_holidays();\r
448         my $exceptions = $self->get_exception_holidays();\r
449         my $singles = $self->get_single_holidays();\r
450 \r
451         if (defined($exceptions->{"$year/$month/$day"})) {\r
452                 return 0;\r
453         } else {                \r
454                 if ((exists($weekDays->{$weekday})) || \r
455                         (exists($dayMonths->{"$month/$day"})) || \r
456                         (exists($singles->{"$year/$month/$day"}))) {                    \r
457                         return 1;\r
458                 } else {\r
459                         return 0;\r
460                 }\r
461         }\r
462 \r
463 }\r
464 \r
465 =item addDate\r
466 \r
467         my ($day, $month, $year) = $calendar->addDate($day, $month, $year, $offset)\r
468 \r
469 C<$day> Is the starting day of the interval.\r
470 \r
471 C<$month> Is the starting month of the interval.\r
472 \r
473 C<$year> Is the starting year of the interval.\r
474 \r
475 C<$offset> Is the number of days that this function has to count from $date.\r
476 \r
477 =cut\r
478 \r
479 sub addDate {\r
480         my ($self, $day, $month, $year, $offset) = @_;\r
481          \r
482         if ($offset < 0) { # In case $offset is negative\r
483                 $offset = $offset*(-1);\r
484         }\r
485 \r
486         my $daysMode = C4::Context->preference('useDaysMode');\r
487         if ($daysMode eq 'normal') {\r
488                 ($year, $month, $day) = &Date::Calc::Add_Delta_Days($year, $month, $day, ($offset - 1));\r
489         } else {\r
490                 while ($offset > 0) {                                                           \r
491                         if (!($self->isHoliday($day, $month, $year))) {\r
492                                 $offset = $offset - 1;                                  \r
493                         }                               \r
494                         if ($offset > 0) {\r
495                                 ($year, $month, $day) = &Date::Calc::Add_Delta_Days($year, $month, $day, 1);\r
496                         }                               \r
497                 }\r
498         }\r
499 \r
500         return($day, $month, $year);    \r
501 }\r
502 \r
503 =item daysBetween\r
504 \r
505         my $daysBetween = $calendar->daysBetween($dayFrom, $monthFrom, $yearFrom,\r
506                                                  $dayTo, $monthTo, $yearTo)\r
507 \r
508 C<$dayFrom> Is the starting day of the interval.\r
509 \r
510 C<$monthFrom> Is the starting month of the interval.\r
511 \r
512 C<$yearFrom> Is the starting year of the interval.\r
513 \r
514 C<$dayTo> Is the ending day of the interval.\r
515 \r
516 C<$monthTo> Is the ending month of the interval.\r
517 \r
518 C<$yearTo> Is the ending year of the interval.\r
519 \r
520 =cut\r
521 \r
522 sub daysBetween {\r
523         my ($self, $dayFrom, $monthFrom, $yearFrom, $dayTo, $monthTo, $yearTo) = @_;\r
524          \r
525         my $daysMode = C4::Context->preference('useDaysMode');\r
526         my $count = 1;\r
527         my $continue = 1;\r
528         if ($daysMode eq 'normal') {\r
529                 while ($continue) {\r
530                         if (($yearFrom != $yearTo) || ($monthFrom != $monthTo) || ($dayFrom != $dayTo)) {\r
531                                 ($yearFrom, $monthFrom, $dayFrom) = &Date::Calc::Add_Delta_Days($yearFrom, $monthFrom, $dayFrom, 1);\r
532                                 $count++;\r
533                         } else {\r
534                                 $continue = 0;  \r
535                         }\r
536                 }               \r
537         } else {\r
538                 while ($continue) {\r
539                         if (($yearFrom != $yearTo) || ($monthFrom != $monthTo) || ($dayFrom != $dayTo)) {\r
540                                 if (!($self->isHoliday($dayFrom, $monthFrom, $yearFrom))) {\r
541                                         $count++;\r
542                                 }       \r
543                                 ($yearFrom, $monthFrom, $dayFrom) = &Date::Calc::Add_Delta_Days($yearFrom, $monthFrom, $dayFrom, 1);                            \r
544                         } else {\r
545                                 $continue = 0;  \r
546                         }\r
547                 }               \r
548         }\r
549         return($count); \r
550 }\r
551 \r
552 1;\r
553 \r
554 __END__\r
555 \r
556 =back\r
557 \r
558 =head1 AUTHOR\r
559 \r
560 Koha Physics Library UNLP <matias_veleda@hotmail.com>\r
561 \r
562 =cut