View | Details | Raw Unified | Return to bug 17015
Collapse All | Expand All

(-)a/C4/Calendar.pm (-713 lines)
Lines 1-713 Link Here
1
package C4::Calendar;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use strict;
19
use warnings;
20
use vars qw(@EXPORT);
21
22
use Carp;
23
use Date::Calc qw( Date_to_Days Today);
24
25
use C4::Context;
26
use Koha::Caches;
27
28
use constant ISO_DATE_FORMAT => "%04d-%02d-%02d";
29
30
=head1 NAME
31
32
C4::Calendar::Calendar - Koha module dealing with holidays.
33
34
=head1 SYNOPSIS
35
36
    use C4::Calendar::Calendar;
37
38
=head1 DESCRIPTION
39
40
This package is used to deal with holidays. Through this package, you can set 
41
all kind of holidays for the library.
42
43
=head1 FUNCTIONS
44
45
=head2 new
46
47
  $calendar = C4::Calendar->new(branchcode => $branchcode);
48
49
Each library branch has its own Calendar.  
50
C<$branchcode> specifies which Calendar you want.
51
52
=cut
53
54
sub new {
55
    my $classname = shift @_;
56
    my %options = @_;
57
    my $self = bless({}, $classname);
58
    foreach my $optionName (keys %options) {
59
        $self->{lc($optionName)} = $options{$optionName};
60
    }
61
    defined($self->{branchcode}) or croak "No branchcode argument to new.  Should be C4::Calendar->new(branchcode => \$branchcode)";
62
    $self->_init($self->{branchcode});
63
    return $self;
64
}
65
66
sub _init {
67
    my $self = shift @_;
68
    my $branch = shift;
69
    defined($branch) or die "No branchcode sent to _init";  # must test for defined here and above to allow ""
70
    my $dbh = C4::Context->dbh();
71
    my $repeatable = $dbh->prepare( 'SELECT *
72
                                       FROM repeatable_holidays
73
                                      WHERE ( branchcode = ? )
74
                                        AND (ISNULL(weekday) = ?)' );
75
    $repeatable->execute($branch,0);
76
    my %week_days_holidays;
77
    while (my $row = $repeatable->fetchrow_hashref) {
78
        my $key = $row->{weekday};
79
        $week_days_holidays{$key}{title}       = $row->{title};
80
        $week_days_holidays{$key}{description} = $row->{description};
81
    }
82
    $self->{'week_days_holidays'} = \%week_days_holidays;
83
84
    $repeatable->execute($branch,1);
85
    my %day_month_holidays;
86
    while (my $row = $repeatable->fetchrow_hashref) {
87
        my $key = $row->{month} . "/" . $row->{day};
88
        $day_month_holidays{$key}{title}       = $row->{title};
89
        $day_month_holidays{$key}{description} = $row->{description};
90
        $day_month_holidays{$key}{day} = sprintf("%02d", $row->{day});
91
        $day_month_holidays{$key}{month} = sprintf("%02d", $row->{month});
92
    }
93
    $self->{'day_month_holidays'} = \%day_month_holidays;
94
95
    my $special = $dbh->prepare( 'SELECT day, month, year, title, description
96
                                    FROM special_holidays
97
                                   WHERE ( branchcode = ? )
98
                                     AND (isexception = ?)' );
99
    $special->execute($branch,1);
100
    my %exception_holidays;
101
    while (my ($day, $month, $year, $title, $description) = $special->fetchrow) {
102
        $exception_holidays{"$year/$month/$day"}{title} = $title;
103
        $exception_holidays{"$year/$month/$day"}{description} = $description;
104
        $exception_holidays{"$year/$month/$day"}{date} = 
105
		sprintf(ISO_DATE_FORMAT, $year, $month, $day);
106
    }
107
    $self->{'exception_holidays'} = \%exception_holidays;
108
109
    $special->execute($branch,0);
110
    my %single_holidays;
111
    while (my ($day, $month, $year, $title, $description) = $special->fetchrow) {
112
        $single_holidays{"$year/$month/$day"}{title} = $title;
113
        $single_holidays{"$year/$month/$day"}{description} = $description;
114
        $single_holidays{"$year/$month/$day"}{date} = 
115
		sprintf(ISO_DATE_FORMAT, $year, $month, $day);
116
    }
117
    $self->{'single_holidays'} = \%single_holidays;
118
    return $self;
119
}
120
121
=head2 get_week_days_holidays
122
123
   $week_days_holidays = $calendar->get_week_days_holidays();
124
125
Returns a hash reference to week days holidays.
126
127
=cut
128
129
sub get_week_days_holidays {
130
    my $self = shift @_;
131
    my $week_days_holidays = $self->{'week_days_holidays'};
132
    return $week_days_holidays;
133
}
134
135
=head2 get_day_month_holidays
136
137
   $day_month_holidays = $calendar->get_day_month_holidays();
138
139
Returns a hash reference to day month holidays.
140
141
=cut
142
143
sub get_day_month_holidays {
144
    my $self = shift @_;
145
    my $day_month_holidays = $self->{'day_month_holidays'};
146
    return $day_month_holidays;
147
}
148
149
=head2 get_exception_holidays
150
151
    $exception_holidays = $calendar->exception_holidays();
152
153
Returns a hash reference to exception holidays. This kind of days are those
154
which stands for a holiday, but you wanted to make an exception for this particular
155
date.
156
157
=cut
158
159
sub get_exception_holidays {
160
    my $self = shift @_;
161
    my $exception_holidays = $self->{'exception_holidays'};
162
    return $exception_holidays;
163
}
164
165
=head2 get_single_holidays
166
167
    $single_holidays = $calendar->get_single_holidays();
168
169
Returns a hash reference to single holidays. This kind of holidays are those which
170
happened just one time.
171
172
=cut
173
174
sub get_single_holidays {
175
    my $self = shift @_;
176
    my $single_holidays = $self->{'single_holidays'};
177
    return $single_holidays;
178
}
179
180
=head2 insert_week_day_holiday
181
182
    insert_week_day_holiday(weekday => $weekday,
183
                            title => $title,
184
                            description => $description);
185
186
Inserts a new week day for $self->{branchcode}.
187
188
C<$day> Is the week day to make holiday.
189
190
C<$title> Is the title to store for the holiday formed by $year/$month/$day.
191
192
C<$description> Is the description to store for the holiday formed by $year/$month/$day.
193
194
=cut
195
196
sub insert_week_day_holiday {
197
    my $self = shift @_;
198
    my %options = @_;
199
200
    my $weekday = $options{weekday};
201
    croak "Invalid weekday $weekday" unless $weekday =~ m/^[0-6]$/;
202
203
    my $dbh = C4::Context->dbh();
204
    my $insertHoliday = $dbh->prepare("insert into repeatable_holidays (branchcode,weekday,day,month,title,description) values ( ?,?,NULL,NULL,?,? )");
205
	$insertHoliday->execute( $self->{branchcode}, $weekday, $options{title}, $options{description});
206
    $self->{'week_days_holidays'}->{$weekday}{title} = $options{title};
207
    $self->{'week_days_holidays'}->{$weekday}{description} = $options{description};
208
    return $self;
209
}
210
211
=head2 insert_day_month_holiday
212
213
    insert_day_month_holiday(day => $day,
214
                             month => $month,
215
                             title => $title,
216
                             description => $description);
217
218
Inserts a new day month holiday for $self->{branchcode}.
219
220
C<$day> Is the day month to make the date to insert.
221
222
C<$month> Is month to make the date to insert.
223
224
C<$title> Is the title to store for the holiday formed by $year/$month/$day.
225
226
C<$description> Is the description to store for the holiday formed by $year/$month/$day.
227
228
=cut
229
230
sub insert_day_month_holiday {
231
    my $self = shift @_;
232
    my %options = @_;
233
234
    my $dbh = C4::Context->dbh();
235
    my $insertHoliday = $dbh->prepare("insert into repeatable_holidays (branchcode,weekday,day,month,title,description) values (?, NULL, ?, ?, ?,? )");
236
	$insertHoliday->execute( $self->{branchcode}, $options{day},$options{month},$options{title}, $options{description});
237
    $self->{'day_month_holidays'}->{"$options{month}/$options{day}"}{title} = $options{title};
238
    $self->{'day_month_holidays'}->{"$options{month}/$options{day}"}{description} = $options{description};
239
    return $self;
240
}
241
242
=head2 insert_single_holiday
243
244
    insert_single_holiday(day => $day,
245
                          month => $month,
246
                          year => $year,
247
                          title => $title,
248
                          description => $description);
249
250
Inserts a new single holiday for $self->{branchcode}.
251
252
C<$day> Is the day month to make the date to insert.
253
254
C<$month> Is month to make the date to insert.
255
256
C<$year> Is year to make the date to insert.
257
258
C<$title> Is the title to store for the holiday formed by $year/$month/$day.
259
260
C<$description> Is the description to store for the holiday formed by $year/$month/$day.
261
262
=cut
263
264
sub insert_single_holiday {
265
    my $self = shift @_;
266
    my %options = @_;
267
    @options{qw(year month day)} = ( $options{date} =~ m/(\d+)-(\d+)-(\d+)/o )
268
      if $options{date} && !$options{day};
269
270
	my $dbh = C4::Context->dbh();
271
    my $isexception = 0;
272
    my $insertHoliday = $dbh->prepare("insert into special_holidays (branchcode,day,month,year,isexception,title,description) values (?,?,?,?,?,?,?)");
273
	$insertHoliday->execute( $self->{branchcode}, $options{day},$options{month},$options{year}, $isexception, $options{title}, $options{description});
274
    $self->{'single_holidays'}->{"$options{year}/$options{month}/$options{day}"}{title} = $options{title};
275
    $self->{'single_holidays'}->{"$options{year}/$options{month}/$options{day}"}{description} = $options{description};
276
277
278
    # changed the 'single_holidays' table, lets force/reset its cache
279
    my $cache = Koha::Caches->get_instance();
280
    $cache->clear_from_cache( 'single_holidays') ;
281
    $cache->clear_from_cache( 'exception_holidays') ;
282
283
    return $self;
284
285
}
286
287
=head2 insert_exception_holiday
288
289
    insert_exception_holiday(day => $day,
290
                             month => $month,
291
                             year => $year,
292
                             title => $title,
293
                             description => $description);
294
295
Inserts a new exception holiday for $self->{branchcode}.
296
297
C<$day> Is the day month to make the date to insert.
298
299
C<$month> Is month to make the date to insert.
300
301
C<$year> Is year to make the date to insert.
302
303
C<$title> Is the title to store for the holiday formed by $year/$month/$day.
304
305
C<$description> Is the description to store for the holiday formed by $year/$month/$day.
306
307
=cut
308
309
sub insert_exception_holiday {
310
    my $self = shift @_;
311
    my %options = @_;
312
313
    @options{qw(year month day)} = ( $options{date} =~ m/(\d+)-(\d+)-(\d+)/o )
314
      if $options{date} && !$options{day};
315
316
    my $dbh = C4::Context->dbh();
317
    my $isexception = 1;
318
    my $insertException = $dbh->prepare("insert into special_holidays (branchcode,day,month,year,isexception,title,description) values (?,?,?,?,?,?,?)");
319
	$insertException->execute( $self->{branchcode}, $options{day},$options{month},$options{year}, $isexception, $options{title}, $options{description});
320
    $self->{'exception_holidays'}->{"$options{year}/$options{month}/$options{day}"}{title} = $options{title};
321
    $self->{'exception_holidays'}->{"$options{year}/$options{month}/$options{day}"}{description} = $options{description};
322
323
    # changed the 'single_holidays' table, lets force/reset its cache
324
    my $cache = Koha::Caches->get_instance();
325
    $cache->clear_from_cache( 'single_holidays') ;
326
    $cache->clear_from_cache( 'exception_holidays') ;
327
328
    return $self;
329
}
330
331
=head2 ModWeekdayholiday
332
333
    ModWeekdayholiday(weekday =>$weekday,
334
                      title => $title,
335
                      description => $description)
336
337
Modifies the title and description of a weekday for $self->{branchcode}.
338
339
C<$weekday> Is the title to update for the holiday.
340
341
C<$description> Is the description to update for the holiday.
342
343
=cut
344
345
sub ModWeekdayholiday {
346
    my $self = shift @_;
347
    my %options = @_;
348
349
    my $dbh = C4::Context->dbh();
350
    my $updateHoliday = $dbh->prepare("UPDATE repeatable_holidays SET title = ?, description = ? WHERE branchcode = ? AND weekday = ?");
351
    $updateHoliday->execute( $options{title},$options{description},$self->{branchcode},$options{weekday}); 
352
    $self->{'week_days_holidays'}->{$options{weekday}}{title} = $options{title};
353
    $self->{'week_days_holidays'}->{$options{weekday}}{description} = $options{description};
354
    return $self;
355
}
356
357
=head2 ModDaymonthholiday
358
359
    ModDaymonthholiday(day => $day,
360
                       month => $month,
361
                       title => $title,
362
                       description => $description);
363
364
Modifies the title and description for a day/month holiday for $self->{branchcode}.
365
366
C<$day> The day of the month for the update.
367
368
C<$month> The month to be used for the update.
369
370
C<$title> The title to be updated for the holiday.
371
372
C<$description> The description to be update for the holiday.
373
374
=cut
375
376
sub ModDaymonthholiday {
377
    my $self = shift @_;
378
    my %options = @_;
379
380
    my $dbh = C4::Context->dbh();
381
    my $updateHoliday = $dbh->prepare("UPDATE repeatable_holidays SET title = ?, description = ? WHERE month = ? AND day = ? AND branchcode = ?");
382
       $updateHoliday->execute( $options{title},$options{description},$options{month},$options{day},$self->{branchcode}); 
383
    $self->{'day_month_holidays'}->{"$options{month}/$options{day}"}{title} = $options{title};
384
    $self->{'day_month_holidays'}->{"$options{month}/$options{day}"}{description} = $options{description};
385
    return $self;
386
}
387
388
=head2 ModSingleholiday
389
390
    ModSingleholiday(day => $day,
391
                     month => $month,
392
                     year => $year,
393
                     title => $title,
394
                     description => $description);
395
396
Modifies the title and description for a single holiday for $self->{branchcode}.
397
398
C<$day> Is the day of the month to make the update.
399
400
C<$month> Is the month to make the update.
401
402
C<$year> Is the year to make the update.
403
404
C<$title> Is the title to update for the holiday formed by $year/$month/$day.
405
406
C<$description> Is the description to update for the holiday formed by $year/$month/$day.
407
408
=cut
409
410
sub ModSingleholiday {
411
    my $self = shift @_;
412
    my %options = @_;
413
414
    my $dbh = C4::Context->dbh();
415
    my $isexception = 0;
416
417
    my $updateHoliday = $dbh->prepare("
418
UPDATE special_holidays SET title = ?, description = ?
419
    WHERE day = ? AND month = ? AND year = ? AND branchcode = ? AND isexception = ?");
420
      $updateHoliday->execute($options{title},$options{description},$options{day},$options{month},$options{year},$self->{branchcode},$isexception);    
421
    $self->{'single_holidays'}->{"$options{year}/$options{month}/$options{day}"}{title} = $options{title};
422
    $self->{'single_holidays'}->{"$options{year}/$options{month}/$options{day}"}{description} = $options{description};
423
424
    # changed the 'single_holidays' table, lets force/reset its cache
425
    my $cache = Koha::Caches->get_instance();
426
    $cache->clear_from_cache( 'single_holidays') ;
427
    $cache->clear_from_cache( 'exception_holidays') ;
428
429
    return $self;
430
}
431
432
=head2 ModExceptionholiday
433
434
    ModExceptionholiday(day => $day,
435
                        month => $month,
436
                        year => $year,
437
                        title => $title,
438
                        description => $description);
439
440
Modifies the title and description for an exception holiday for $self->{branchcode}.
441
442
C<$day> Is the day of the month for the holiday.
443
444
C<$month> Is the month for the holiday.
445
446
C<$year> Is the year for the holiday.
447
448
C<$title> Is the title to be modified for the holiday formed by $year/$month/$day.
449
450
C<$description> Is the description to be modified for the holiday formed by $year/$month/$day.
451
452
=cut
453
454
sub ModExceptionholiday {
455
    my $self = shift @_;
456
    my %options = @_;
457
458
    my $dbh = C4::Context->dbh();
459
    my $isexception = 1;
460
    my $updateHoliday = $dbh->prepare("
461
UPDATE special_holidays SET title = ?, description = ?
462
    WHERE day = ? AND month = ? AND year = ? AND branchcode = ? AND isexception = ?");
463
    $updateHoliday->execute($options{title},$options{description},$options{day},$options{month},$options{year},$self->{branchcode},$isexception);
464
    $self->{'exception_holidays'}->{"$options{year}/$options{month}/$options{day}"}{title} = $options{title};
465
    $self->{'exception_holidays'}->{"$options{year}/$options{month}/$options{day}"}{description} = $options{description};
466
467
    # changed the 'single_holidays' table, lets force/reset its cache
468
    my $cache = Koha::Caches->get_instance();
469
    $cache->clear_from_cache( 'single_holidays') ;
470
    $cache->clear_from_cache( 'exception_holidays') ;
471
472
    return $self;
473
}
474
475
=head2 delete_holiday
476
477
    delete_holiday(weekday => $weekday
478
                   day => $day,
479
                   month => $month,
480
                   year => $year);
481
482
Delete a holiday for $self->{branchcode}.
483
484
C<$weekday> Is the week day to delete.
485
486
C<$day> Is the day month to make the date to delete.
487
488
C<$month> Is month to make the date to delete.
489
490
C<$year> Is year to make the date to delete.
491
492
=cut
493
494
sub delete_holiday {
495
    my $self = shift @_;
496
    my %options = @_;
497
498
    # Verify what kind of holiday that day is. For example, if it is
499
    # a repeatable holiday, this should check if there are some exception
500
    # for that holiday rule. Otherwise, if it is a regular holiday, it´s
501
    # ok just deleting it.
502
503
    my $dbh = C4::Context->dbh();
504
    my $isSingleHoliday = $dbh->prepare("SELECT id FROM special_holidays WHERE (branchcode = ?) AND (day = ?) AND (month = ?) AND (year = ?)");
505
    $isSingleHoliday->execute($self->{branchcode}, $options{day}, $options{month}, $options{year});
506
    if ($isSingleHoliday->rows) {
507
        my $id = $isSingleHoliday->fetchrow;
508
        $isSingleHoliday->finish; # Close the last query
509
510
        my $deleteHoliday = $dbh->prepare("DELETE FROM special_holidays WHERE id = ?");
511
        $deleteHoliday->execute($id);
512
        delete($self->{'single_holidays'}->{"$options{year}/$options{month}/$options{day}"});
513
    } else {
514
        $isSingleHoliday->finish; # Close the last query
515
516
        my $isWeekdayHoliday = $dbh->prepare("SELECT id FROM repeatable_holidays WHERE branchcode = ? AND weekday = ?");
517
        $isWeekdayHoliday->execute($self->{branchcode}, $options{weekday});
518
        if ($isWeekdayHoliday->rows) {
519
            my $id = $isWeekdayHoliday->fetchrow;
520
            $isWeekdayHoliday->finish; # Close the last query
521
522
            my $updateExceptions = $dbh->prepare("UPDATE special_holidays SET isexception = 0 WHERE (WEEKDAY(CONCAT(special_holidays.year,'-',special_holidays.month,'-',special_holidays.day)) = ?) AND (branchcode = ?)");
523
            $updateExceptions->execute($options{weekday}, $self->{branchcode});
524
            $updateExceptions->finish; # Close the last query
525
526
            my $deleteHoliday = $dbh->prepare("DELETE FROM repeatable_holidays WHERE id = ?");
527
            $deleteHoliday->execute($id);
528
            delete($self->{'week_days_holidays'}->{$options{weekday}});
529
        } else {
530
            $isWeekdayHoliday->finish; # Close the last query
531
532
            my $isDayMonthHoliday = $dbh->prepare("SELECT id FROM repeatable_holidays WHERE (branchcode = ?) AND (day = ?) AND (month = ?)");
533
            $isDayMonthHoliday->execute($self->{branchcode}, $options{day}, $options{month});
534
            if ($isDayMonthHoliday->rows) {
535
                my $id = $isDayMonthHoliday->fetchrow;
536
                $isDayMonthHoliday->finish;
537
                my $updateExceptions = $dbh->prepare("UPDATE special_holidays SET isexception = 0 WHERE (special_holidays.branchcode = ?) AND (special_holidays.day = ?) and (special_holidays.month = ?)");
538
                $updateExceptions->execute($self->{branchcode}, $options{day}, $options{month});
539
                $updateExceptions->finish; # Close the last query
540
541
                my $deleteHoliday = $dbh->prepare("DELETE FROM repeatable_holidays WHERE (id = ?)");
542
                $deleteHoliday->execute($id);
543
                delete($self->{'day_month_holidays'}->{"$options{month}/$options{day}"});
544
            }
545
        }
546
    }
547
548
    # changed the 'single_holidays' table, lets force/reset its cache
549
    my $cache = Koha::Caches->get_instance();
550
    $cache->clear_from_cache( 'single_holidays') ;
551
    $cache->clear_from_cache( 'exception_holidays') ;
552
553
    return $self;
554
}
555
=head2 delete_holiday_range
556
557
    delete_holiday_range(day => $day,
558
                   month => $month,
559
                   year => $year);
560
561
Delete a holiday range of dates for $self->{branchcode}.
562
563
C<$day> Is the day month to make the date to delete.
564
565
C<$month> Is month to make the date to delete.
566
567
C<$year> Is year to make the date to delete.
568
569
=cut
570
571
sub delete_holiday_range {
572
    my $self = shift;
573
    my %options = @_;
574
575
    my $dbh = C4::Context->dbh();
576
    my $sth = $dbh->prepare("DELETE FROM special_holidays WHERE (branchcode = ?) AND (day = ?) AND (month = ?) AND (year = ?)");
577
    $sth->execute($self->{branchcode}, $options{day}, $options{month}, $options{year});
578
579
    # changed the 'single_holidays' table, lets force/reset its cache
580
    my $cache = Koha::Caches->get_instance();
581
    $cache->clear_from_cache( 'single_holidays') ;
582
    $cache->clear_from_cache( 'exception_holidays') ;
583
584
}
585
586
=head2 delete_holiday_range_repeatable
587
588
    delete_holiday_range_repeatable(day => $day,
589
                   month => $month);
590
591
Delete a holiday for $self->{branchcode}.
592
593
C<$day> Is the day month to make the date to delete.
594
595
C<$month> Is month to make the date to delete.
596
597
=cut
598
599
sub delete_holiday_range_repeatable {
600
    my $self = shift;
601
    my %options = @_;
602
603
    my $dbh = C4::Context->dbh();
604
    my $sth = $dbh->prepare("DELETE FROM repeatable_holidays WHERE (branchcode = ?) AND (day = ?) AND (month = ?)");
605
    $sth->execute($self->{branchcode}, $options{day}, $options{month});
606
}
607
608
=head2 delete_exception_holiday_range
609
610
    delete_exception_holiday_range(weekday => $weekday
611
                   day => $day,
612
                   month => $month,
613
                   year => $year);
614
615
Delete a holiday for $self->{branchcode}.
616
617
C<$day> Is the day month to make the date to delete.
618
619
C<$month> Is month to make the date to delete.
620
621
C<$year> Is year to make the date to delete.
622
623
=cut
624
625
sub delete_exception_holiday_range {
626
    my $self = shift;
627
    my %options = @_;
628
629
    my $dbh = C4::Context->dbh();
630
    my $sth = $dbh->prepare("DELETE FROM special_holidays WHERE (branchcode = ?) AND (isexception = 1) AND (day = ?) AND (month = ?) AND (year = ?)");
631
    $sth->execute($self->{branchcode}, $options{day}, $options{month}, $options{year});
632
633
    # changed the 'single_holidays' table, lets force/reset its cache
634
    my $cache = Koha::Caches->get_instance();
635
    $cache->clear_from_cache( 'single_holidays') ;
636
    $cache->clear_from_cache( 'exception_holidays') ;
637
}
638
639
=head2 isHoliday
640
641
    $isHoliday = isHoliday($day, $month $year);
642
643
C<$day> Is the day to check whether if is a holiday or not.
644
645
C<$month> Is the month to check whether if is a holiday or not.
646
647
C<$year> Is the year to check whether if is a holiday or not.
648
649
=cut
650
651
sub isHoliday {
652
    my ($self, $day, $month, $year) = @_;
653
	# FIXME - date strings are stored in non-padded metric format. should change to iso.
654
	$month=$month+0;
655
	$year=$year+0;
656
	$day=$day+0;
657
    my $weekday = &Date::Calc::Day_of_Week($year, $month, $day) % 7;
658
    my $weekDays   = $self->get_week_days_holidays();
659
    my $dayMonths  = $self->get_day_month_holidays();
660
    my $exceptions = $self->get_exception_holidays();
661
    my $singles    = $self->get_single_holidays();
662
    if (defined($exceptions->{"$year/$month/$day"})) {
663
        return 0;
664
    } else {
665
        if ((exists($weekDays->{$weekday})) ||
666
            (exists($dayMonths->{"$month/$day"})) ||
667
            (exists($singles->{"$year/$month/$day"}))) {
668
            return 1;
669
        } else {
670
            return 0;
671
        }
672
    }
673
674
}
675
676
=head2 copy_to_branch
677
678
    $calendar->copy_to_branch($target_branch)
679
680
=cut
681
682
sub copy_to_branch {
683
    my ($self, $target_branch) = @_;
684
685
    croak "No target_branch" unless $target_branch;
686
687
    my $target_calendar = C4::Calendar->new(branchcode => $target_branch);
688
689
    my ($y, $m, $d) = Today();
690
    my $today = sprintf ISO_DATE_FORMAT, $y,$m,$d;
691
692
    my $wdh = $self->get_week_days_holidays;
693
    $target_calendar->insert_week_day_holiday( weekday => $_, %{ $wdh->{$_} } )
694
      foreach keys %$wdh;
695
    $target_calendar->insert_day_month_holiday(%$_)
696
      foreach values %{ $self->get_day_month_holidays };
697
    $target_calendar->insert_exception_holiday(%$_)
698
      foreach grep { $_->{date} gt $today } values %{ $self->get_exception_holidays };
699
    $target_calendar->insert_single_holiday(%$_)
700
      foreach grep { $_->{date} gt $today } values %{ $self->get_single_holidays };
701
702
    return 1;
703
}
704
705
1;
706
707
__END__
708
709
=head1 AUTHOR
710
711
Koha Physics Library UNLP <matias_veleda@hotmail.com>
712
713
=cut
(-)a/C4/Circulation.pm (-7 / +6 lines)
Lines 1198-1204 sub checkHighHolds { Link Here
1198
1198
1199
        my $issuedate = DateTime->now( time_zone => C4::Context->tz() );
1199
        my $issuedate = DateTime->now( time_zone => C4::Context->tz() );
1200
1200
1201
        my $calendar = Koha::DiscreteCalendar->new( branchcode => $branch );
1201
        my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branch });
1202
1202
1203
        my $itype = $item_object->effective_itemtype;
1203
        my $itype = $item_object->effective_itemtype;
1204
        my $orig_due = C4::Circulation::CalcDateDue( $issuedate, $itype, $branch, $borrower );
1204
        my $orig_due = C4::Circulation::CalcDateDue( $issuedate, $itype, $branch, $borrower );
Lines 1725-1731 Returns a book. Link Here
1725
removed. Optional.
1725
removed. Optional.
1726
1726
1727
=item C<$dropbox> indicates that the check-in date is assumed to be
1727
=item C<$dropbox> indicates that the check-in date is assumed to be
1728
yesterday, or the last non-holiday as defined in C4::Calendar .  If
1728
yesterday, or the last non-holiday as defined in Koha::DiscreteCalendar. If
1729
overdue charges are applied and C<$dropbox> is true, the last charge
1729
overdue charges are applied and C<$dropbox> is true, the last charge
1730
will be removed.  This assumes that the fines accrual script has run
1730
will be removed.  This assumes that the fines accrual script has run
1731
for _today_. Optional.
1731
for _today_. Optional.
Lines 2128-2134 sub MarkIssueReturned { Link Here
2128
    my $query = 'UPDATE issues SET returndate=';
2128
    my $query = 'UPDATE issues SET returndate=';
2129
    my @bind;
2129
    my @bind;
2130
    if ($dropbox_branch) {
2130
    if ($dropbox_branch) {
2131
        my $calendar = Koha::DiscreteCalendar->new( branchcode => $dropbox_branch );
2131
        my $calendar = Koha::DiscreteCalendar->new({ branchcode => $dropbox_branch });
2132
        my $dropboxdate = $calendar->addDate( DateTime->now( time_zone => C4::Context->tz), -1 );
2132
        my $dropboxdate = $calendar->addDate( DateTime->now( time_zone => C4::Context->tz), -1 );
2133
        $query .= ' ? ';
2133
        $query .= ' ? ';
2134
        push @bind, $dropboxdate->strftime('%Y-%m-%d %H:%M');
2134
        push @bind, $dropboxdate->strftime('%Y-%m-%d %H:%M');
Lines 3482-3488 sub CalcDateDue { Link Here
3482
        else { # days
3482
        else { # days
3483
            $dur = DateTime::Duration->new( days => $loanlength->{$length_key});
3483
            $dur = DateTime::Duration->new( days => $loanlength->{$length_key});
3484
        }
3484
        }
3485
        my $calendar = Koha::DiscreteCalendar->new( branchcode => $branch );
3485
        my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branch });
3486
        $datedue = $calendar->addDate( $datedue, $dur, $loanlength->{lengthunit} );
3486
        $datedue = $calendar->addDate( $datedue, $dur, $loanlength->{lengthunit} );
3487
        if ($loanlength->{lengthunit} eq 'days') {
3487
        if ($loanlength->{lengthunit} eq 'days') {
3488
            $datedue->set_hour(23);
3488
            $datedue->set_hour(23);
Lines 3521-3534 sub CalcDateDue { Link Here
3521
            }
3521
            }
3522
        }
3522
        }
3523
        if ( C4::Context->preference('useDaysMode') ne 'Days' ) {
3523
        if ( C4::Context->preference('useDaysMode') ne 'Days' ) {
3524
          my $calendar = Koha::DiscreteCalendar->new( branchcode => $branch );
3524
          my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branch });
3525
          if ( $calendar->is_holiday($datedue) ) {
3525
          if ( $calendar->is_holiday($datedue) ) {
3526
              # Don't return on a closed day
3526
              # Don't return on a closed day
3527
              $datedue = $calendar->prev_open_day( $datedue );
3527
              $datedue = $calendar->prev_open_day( $datedue )->set(hour => 23, minute => 59);
3528
          }
3528
          }
3529
        }
3529
        }
3530
    }
3530
    }
3531
3532
    return $datedue;
3531
    return $datedue;
3533
}
3532
}
3534
3533
(-)a/C4/HoldsQueue.pm (-2 / +2 lines)
Lines 77-83 sub TransportCostMatrix { Link Here
77
        };
77
        };
78
78
79
        if ( C4::Context->preference("HoldsQueueSkipClosed") ) {
79
        if ( C4::Context->preference("HoldsQueueSkipClosed") ) {
80
            $calendars->{$from} ||= Koha::DiscreteCalendar->new( branchcode => $from );
80
            $calendars->{$from} ||= Koha::DiscreteCalendar->new({ branchcode => $from });
81
            $transport_cost_matrix{$to}{$from}{disable_transfer} ||=
81
            $transport_cost_matrix{$to}{$from}{disable_transfer} ||=
82
              $calendars->{$from}->is_holiday( $today );
82
              $calendars->{$from}->is_holiday( $today );
83
        }
83
        }
Lines 742-748 sub load_branches_to_pull_from { Link Here
742
    my $today = dt_from_string();
742
    my $today = dt_from_string();
743
    if ( C4::Context->preference('HoldsQueueSkipClosed') ) {
743
    if ( C4::Context->preference('HoldsQueueSkipClosed') ) {
744
        @branches_to_use = grep {
744
        @branches_to_use = grep {
745
            !Koha::DiscreteCalendar->new( branchcode => $_ )
745
            !Koha::DiscreteCalendar->new({ branchcode => $_ })
746
              ->is_holiday( $today )
746
              ->is_holiday( $today )
747
        } @branches_to_use;
747
        } @branches_to_use;
748
    }
748
    }
(-)a/C4/Overdues.pm (-2 / +2 lines)
Lines 294-300 sub get_chargeable_units { Link Here
294
    my $charge_duration;
294
    my $charge_duration;
295
    if ($unit eq 'hours') {
295
    if ($unit eq 'hours') {
296
        if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
296
        if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
297
            my $calendar = Koha::DiscreteCalendar->new( branchcode => $branchcode );
297
            my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branchcode });
298
            $charge_duration = $calendar->hours_between( $date_due, $date_returned );
298
            $charge_duration = $calendar->hours_between( $date_due, $date_returned );
299
        } else {
299
        } else {
300
            $charge_duration = $date_returned->delta_ms( $date_due );
300
            $charge_duration = $date_returned->delta_ms( $date_due );
Lines 306-312 sub get_chargeable_units { Link Here
306
    }
306
    }
307
    else { # days
307
    else { # days
308
        if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
308
        if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
309
            my $calendar = Koha::DiscreteCalendar->new( branchcode => $branchcode );
309
            my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branchcode });
310
            $charge_duration = $calendar->days_between( $date_due, $date_returned );
310
            $charge_duration = $calendar->days_between( $date_due, $date_returned );
311
        } else {
311
        } else {
312
            $charge_duration = $date_returned->delta_days( $date_due );
312
            $charge_duration = $date_returned->delta_days( $date_due );
(-)a/C4/Reserves.pm (-1 / +1 lines)
Lines 781-787 sub CancelExpiredReserves { Link Here
781
    my $holds = Koha::Holds->search( $params );
781
    my $holds = Koha::Holds->search( $params );
782
782
783
    while ( my $hold = $holds->next ) {
783
    while ( my $hold = $holds->next ) {
784
        my $calendar = Koha::DiscreteCalendar->new( branchcode => $hold->branchcode );
784
        my $calendar = Koha::DiscreteCalendar->new({ branchcode => $hold->branchcode });
785
785
786
        next if !$cancel_on_holidays && $calendar->is_holiday( $today );
786
        next if !$cancel_on_holidays && $calendar->is_holiday( $today );
787
787
(-)a/Koha/DiscreteCalendar.pm (-30 / +34 lines)
Lines 16-28 package Koha::DiscreteCalendar; Link Here
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
17
18
#####Sets holiday periods for each branch. Datedues will be extended if branch is closed -TG
18
#####Sets holiday periods for each branch. Datedues will be extended if branch is closed -TG
19
use strict;
19
use Modern::Perl;
20
use warnings;
21
20
22
use CGI qw ( -utf8 );
21
use CGI qw ( -utf8 );
23
use Carp;
22
use Carp;
24
use DateTime;
23
use DateTime;
25
use DateTime::Format::Strptime;
24
use DateTime::Format::Strptime;
25
use Data::Dumper;
26
26
27
use C4::Context;
27
use C4::Context;
28
use C4::Output;
28
use C4::Output;
Lines 48-54 Koha::DiscreteCalendar - Object containing a branches calendar, working with the Link Here
48
48
49
  use Koha::DiscreteCalendar
49
  use Koha::DiscreteCalendar
50
50
51
  my $c = Koha::DiscreteCalendar->new( branchcode => 'MAIN' );
51
  my $c = Koha::DiscreteCalendar->new({ branchcode => 'MAIN' });
52
  my $dt = DateTime->now();
52
  my $dt = DateTime->now();
53
53
54
  # are we open
54
  # are we open
Lines 66-84 Koha::DiscreteCalendar - Object containing a branches calendar, working with the Link Here
66
66
67
=head2 new : Create a (discrete) calendar object
67
=head2 new : Create a (discrete) calendar object
68
68
69
my $calendar = Koha::DiscreteCalendar->new( branchcode => 'MAIN' );
69
my $calendar = Koha::DiscreteCalendar->new({ branchcode => 'MAIN' });
70
70
71
The option branchcode is required
71
The option branchcode is required
72
72
73
=cut
73
=cut
74
74
75
sub new {
75
sub new {
76
    my ( $classname, %options ) = @_;
76
    my ( $classname, $options ) = @_;
77
    my $self = {};
77
    my $self = {};
78
    bless $self, $classname;
78
    bless $self, $classname;
79
    for my $o_name ( keys %options ) {
79
    for my $o_name ( keys %{ $options } ) {
80
        my $o = lc $o_name;
80
        my $o = lc $o_name;
81
        $self->{$o} = $options{$o_name};
81
        $self->{$o} = $options->{$o_name};
82
    }
82
    }
83
    if ( !defined $self->{branchcode} ) {
83
    if ( !defined $self->{branchcode} ) {
84
        croak 'No branchcode argument passed to Koha::DiscreteCalendar->new';
84
        croak 'No branchcode argument passed to Koha::DiscreteCalendar->new';
Lines 157-163 sub get_dates_info { Link Here
157
157
158
    Koha::DiscreteCalendar->add_new_branch($copyBranch, $newBranch)
158
    Koha::DiscreteCalendar->add_new_branch($copyBranch, $newBranch)
159
159
160
This methode will copy everything from a given branch to a new branch
160
This method will copy everything from a given branch to a new branch
161
C<$copyBranch> is the branch to copy from
161
C<$copyBranch> is the branch to copy from
162
C<$newBranch> is the branch to be created, and copy into
162
C<$newBranch> is the branch to be created, and copy into
163
163
Lines 240-246 Returns the furthest date available in the databse of current branch. Link Here
240
240
241
sub get_max_date {
241
sub get_max_date {
242
    my $self       = shift;
242
    my $self       = shift;
243
    my $branchcode     = $self->{branchcode};
243
    my $branchcode = $self->{branchcode};
244
    my $schema = Koha::Database->new->schema;
244
    my $schema = Koha::Database->new->schema;
245
245
246
    my $rs = $schema->resultset('DiscreteCalendar')->search(
246
    my $rs = $schema->resultset('DiscreteCalendar')->search(
Lines 505-527 sub edit_holiday { Link Here
505
    my $close_hour   = $params->{close_hour} || '';
505
    my $close_hour   = $params->{close_hour} || '';
506
506
507
    my $delete_type  = $params->{delete_type} || undef;
507
    my $delete_type  = $params->{delete_type} || undef;
508
    my $today        = $params->{today} || DateTime->today;
508
    my $today        = $params->{today} || dt_from_string()->truncate( to => 'day' );
509
509
510
    my $branchcode = $self->{branchcode};
510
    my $branchcode = $self->{branchcode};
511
511
512
    # When override param is set, this function will allow past dates to be set as holidays,
513
    # otherwise it will not. This is meant to only be used for testing.
514
    my $override = $params->{override} || 0;
515
512
    my $schema = Koha::Database->new->schema;
516
    my $schema = Koha::Database->new->schema;
513
    $schema->{AutoCommit} = 0;
517
    $schema->{AutoCommit} = 0;
514
    $schema->storage->txn_begin;
518
    $schema->storage->txn_begin;
515
    my $dtf = $schema->storage->datetime_parser;
519
    my $dtf = DateTime::Format::Strptime->new(pattern => "%F %T");
516
520
517
    #String dates for Database usage
521
    #String dates for Database usage
518
    my $start_date_string = $dtf->format_datetime($start_date);
522
    my $start_date_string = $dtf->format_datetime($start_date->clone->truncate(to => 'day'));
519
    my $end_date_string = $dtf->format_datetime($end_date);
523
    my $end_date_string = $dtf->format_datetime($end_date->clone->truncate(to => 'day'));
520
    $today = $dtf->format_datetime($today);
524
    $today = $dtf->format_datetime($today->clone->truncate(to => 'day'));
521
522
    my %updateValues = (
525
    my %updateValues = (
523
        is_opened    => 0,
526
        is_opened    => 0,
524
        note        => $title,
527
        note         => $title,
525
        holiday_type => $holiday_type,
528
        holiday_type => $holiday_type,
526
    );
529
    );
527
    $updateValues{open_hour}  = $open_hour if $open_hour ne '';
530
    $updateValues{open_hour}  = $open_hour if $open_hour ne '';
Lines 537-543 sub edit_holiday { Link Here
537
                branchcode  => $branchcode,
540
                branchcode  => $branchcode,
538
            },
541
            },
539
            {
542
            {
540
                where => \[ 'DAYOFWEEK(date) = ? AND date >= ? AND date <= DATE(?)', $weekday, $start_date_string, $end_date_string],
543
                where => \[ 'DAYOFWEEK(date) = ? AND date >= DATE(?) AND date <= DATE(?)', $weekday, $start_date_string, $end_date_string],
541
            }
544
            }
542
        );
545
        );
543
546
Lines 546-555 sub edit_holiday { Link Here
546
        }
549
        }
547
    }elsif ($holiday_type eq $HOLIDAYS->{EXCEPTION} || $holiday_type eq $HOLIDAYS->{FLOAT} || $holiday_type eq $HOLIDAYS->{NEED_VALIDATION}) {
550
    }elsif ($holiday_type eq $HOLIDAYS->{EXCEPTION} || $holiday_type eq $HOLIDAYS->{FLOAT} || $holiday_type eq $HOLIDAYS->{NEED_VALIDATION}) {
548
        #Update Exception Float and Needs Validation holidays
551
        #Update Exception Float and Needs Validation holidays
549
        my $where = { date => { -between => [$start_date_string, $end_date_string], '>=' => $today}};
552
        my $where = { date => { -between => [$start_date_string, $end_date_string]}};
550
        if($start_date_string ne $end_date_string && $weekday && $weekday ne 'everyday'){
553
        if($start_date_string ne $end_date_string && $weekday && $weekday ne 'everyday'){
551
            $where = {-and => [ \["DAYOFWEEK(date) = ?", $weekday], date => { -between => [$start_date_string, $end_date_string], '>=' => $today}]};
554
            $where = {-and => [ \["DAYOFWEEK(date) = ?", $weekday], date => { -between => [$start_date_string, $end_date_string]}]};
552
        }
555
        }
556
        $where->{date}{'>='} = $today unless $override;
557
553
        my $rs = $schema->resultset('DiscreteCalendar')->search(
558
        my $rs = $schema->resultset('DiscreteCalendar')->search(
554
            {
559
            {
555
                branchcode  => $branchcode,
560
                branchcode  => $branchcode,
Lines 585-594 sub edit_holiday { Link Here
585
590
586
    }else {
591
    }else {
587
        #Update date(s)/Remove holidays
592
        #Update date(s)/Remove holidays
588
        my $where = { date => { -between => [$start_date_string, $end_date_string], '>=' => $today}};
593
        my $where = { date => { -between => [$start_date_string, $end_date_string]}};
589
        if($start_date_string ne $end_date_string && $weekday && $weekday ne 'everyday'){
594
        if($start_date_string ne $end_date_string && $weekday && $weekday ne 'everyday'){
590
            $where = {-and => [ \["DAYOFWEEK(date) = ?", $weekday], date => { -between => [$start_date_string, $end_date_string], '>=' => $today}]};
595
            $where = {-and => [ \["DAYOFWEEK(date) = ?", $weekday], date => { -between => [$start_date_string, $end_date_string]}]};
591
        }
596
        }
597
        $where->{date}{'>='} = $today unless $override;
598
592
        my $rs = $schema->resultset('DiscreteCalendar')->search(
599
        my $rs = $schema->resultset('DiscreteCalendar')->search(
593
            {
600
            {
594
                branchcode  => $branchcode,
601
                branchcode  => $branchcode,
Lines 794-802 sub is_holiday { Link Here
794
        }
801
        }
795
    );
802
    );
796
803
797
    if($rs->count() != 0){
804
    if ($rs->count() != 0) {
798
        $isHoliday = 0 if $rs->first()->is_opened();
805
        $isHoliday = ($rs->first()->is_opened() ? 0 : 1);
799
        $isHoliday = 1 if !$rs->first()->is_opened();
800
    }
806
    }
801
807
802
    return $isHoliday;
808
    return $isHoliday;
Lines 955-969 sub days_between { Link Here
955
961
956
    if ( $start_date->compare($end_date) > 0 ) {
962
    if ( $start_date->compare($end_date) > 0 ) {
957
        # swap dates
963
        # swap dates
958
        my $int_dt = $end_date;
964
        ($start_date, $end_date) = ($end_date, $start_date);
959
        $end_date = $start_date;
960
        $start_date = $int_dt;
961
    }
965
    }
962
966
963
    my $schema = Koha::Database->new->schema;
967
    my $schema = Koha::Database->new->schema;
964
    my $dtf = $schema->storage->datetime_parser;
968
    my $dtf = $schema->storage->datetime_parser;
965
    $start_date = $dtf->format_datetime($start_date);
969
    $start_date = $dtf->format_datetime($start_date->clone->truncate(to => 'day'));
966
    $end_date = $dtf->format_datetime($end_date);
970
    $end_date = $dtf->format_datetime($end_date->clone->truncate(to => 'day'));
967
971
968
    my $days_between = $schema->resultset('DiscreteCalendar')->search(
972
    my $days_between = $schema->resultset('DiscreteCalendar')->search(
969
        {
973
        {
Lines 1118-1124 sub open_hours_between { Link Here
1118
    my ($self, $start_date, $end_date) = @_;
1122
    my ($self, $start_date, $end_date) = @_;
1119
    my $branchcode = $self->{branchcode};
1123
    my $branchcode = $self->{branchcode};
1120
    my $schema = Koha::Database->new->schema;
1124
    my $schema = Koha::Database->new->schema;
1121
    my $dtf = $schema->storage->datetime_parser;
1125
    my $dtf = DateTime::Format::Strptime->new(pattern => "%F %T");
1122
    $start_date = $dtf->format_datetime($start_date);
1126
    $start_date = $dtf->format_datetime($start_date);
1123
    $end_date = $dtf->format_datetime($end_date);
1127
    $end_date = $dtf->format_datetime($end_date);
1124
1128
(-)a/Koha/Hold.pm (-2 / +2 lines)
Lines 62-68 sub age { Link Here
62
    my $age;
62
    my $age;
63
63
64
    if ( $use_calendar ) {
64
    if ( $use_calendar ) {
65
        my $calendar = Koha::DiscreteCalendar->new( branchcode => $self->branchcode );
65
        my $calendar = Koha::DiscreteCalendar->new({ branchcode => $self->branchcode });
66
        $age = $calendar->days_between( dt_from_string( $self->reservedate ), $today );
66
        $age = $calendar->days_between( dt_from_string( $self->reservedate ), $today );
67
    }
67
    }
68
    else {
68
    else {
Lines 164-170 sub set_waiting { Link Here
164
164
165
    my $max_pickup_delay = C4::Context->preference("ReservesMaxPickUpDelay");
165
    my $max_pickup_delay = C4::Context->preference("ReservesMaxPickUpDelay");
166
    my $cancel_on_holidays = C4::Context->preference('ExpireReservesOnHolidays');
166
    my $cancel_on_holidays = C4::Context->preference('ExpireReservesOnHolidays');
167
    my $calendar = Koha::DiscreteCalendar->new( branchcode => $self->branchcode );
167
    my $calendar = Koha::DiscreteCalendar->new({ branchcode => $self->branchcode });
168
168
169
    my $expirationdate = $today->clone;
169
    my $expirationdate = $today->clone;
170
    $expirationdate->add(days => $max_pickup_delay);
170
    $expirationdate->add(days => $max_pickup_delay);
(-)a/circ/returns.pl (-1 / +1 lines)
Lines 204-210 my $dropboxmode = $query->param('dropboxmode'); Link Here
204
my $dotransfer  = $query->param('dotransfer');
204
my $dotransfer  = $query->param('dotransfer');
205
my $canceltransfer = $query->param('canceltransfer');
205
my $canceltransfer = $query->param('canceltransfer');
206
my $dest = $query->param('dest');
206
my $dest = $query->param('dest');
207
my $calendar    = Koha::DiscreteCalendar->new( branchcode => $userenv_branch );
207
my $calendar    = Koha::DiscreteCalendar->new({ branchcode => $userenv_branch });
208
#dropbox: get last open day (today - 1)
208
#dropbox: get last open day (today - 1)
209
my $today       = DateTime->now( time_zone => C4::Context->tz());
209
my $today       = DateTime->now( time_zone => C4::Context->tz());
210
my $dropboxdate = $calendar->addDate($today, -1);
210
my $dropboxdate = $calendar->addDate($today, -1);
(-)a/installer/data/mysql/atomicupdate/bug_17015_part1_create_discrete_calendar.sql (-1 / +1 lines)
Lines 11-14 CREATE TABLE `discrete_calendar` ( Link Here
11
    `open_hour` time NOT NULL,
11
    `open_hour` time NOT NULL,
12
    `close_hour` time NOT NULL,
12
    `close_hour` time NOT NULL,
13
    PRIMARY KEY (`branchcode`,`date`)
13
    PRIMARY KEY (`branchcode`,`date`)
14
);
14
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
(-)a/installer/data/mysql/atomicupdate/bug_17015_part2_fill_discrete_calendar.perl (-19 / +1 lines)
Lines 3-10 Link Here
3
#
3
#
4
#   Script that fills the discrete_calendar table with dates, using the other date-related tables
4
#   Script that fills the discrete_calendar table with dates, using the other date-related tables
5
#
5
#
6
use strict;
6
use Modern::Perl;
7
use warnings;
8
use DateTime;
7
use DateTime;
9
use DateTime::Format::Strptime;
8
use DateTime::Format::Strptime;
10
use Data::Dumper;
9
use Data::Dumper;
Lines 12-36 use Getopt::Long; Link Here
12
use C4::Context;
11
use C4::Context;
13
12
14
# Options
13
# Options
15
my $help = 0;
16
my $daysInFuture = 365;
14
my $daysInFuture = 365;
17
GetOptions (
18
            'days|?|d=i' => \$daysInFuture,
19
            'help|?|h' => \$help);
20
my $usage = << 'ENDUSAGE';
21
15
22
Script that manages the discrete_calendar table.
23
24
This script has the following parameters :
25
    --days --d : how many days in the future will be created, by default it's 365
26
    -h --help: this message
27
28
ENDUSAGE
29
30
if ($help) {
31
    print $usage;
32
    exit;
33
}
34
my $dbh = C4::Context->dbh;
16
my $dbh = C4::Context->dbh;
35
$dbh->{AutoCommit} = 0;
17
$dbh->{AutoCommit} = 0;
36
$dbh->{RaiseError} = 1;
18
$dbh->{RaiseError} = 1;
(-)a/installer/data/mysql/atomicupdate/bug_17015_part3_drop_calendar.sql (+5 lines)
Line 0 Link Here
1
-- Bugzilla 17015
2
-- New koha calendar
3
-- Drop deprecated calendar-related tables after creating and filling discrete_calendar
4
DROP TABLE IF EXISTS `repeatable_holidays`;
5
DROP TABLE IF EXISTS `special_holidays`;
(-)a/installer/data/mysql/kohastructure.sql (-33 / +16 lines)
Lines 680-685 CREATE TABLE `deleteditems` ( Link Here
680
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
680
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
681
681
682
--
682
--
683
-- Table structure for table `discrete_calendar`
684
--
685
686
DROP TABLE IF EXISTS `discrete_calendar`;
687
CREATE TABLE `discrete_calendar` (
688
    `date` datetime NOT NULL,
689
    `branchcode` varchar(10) NOT NULL,
690
    `is_opened` tinyint(1) DEFAULT 1,
691
    `holiday_type` varchar(1) DEFAULT '',
692
    `note` varchar(30) DEFAULT '',
693
    `open_hour` time NOT NULL,
694
    `close_hour` time NOT NULL,
695
    PRIMARY KEY (`branchcode`,`date`)
696
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
697
698
--
683
-- Table structure for table `export_format`
699
-- Table structure for table `export_format`
684
--
700
--
685
701
Lines 1379-1400 CREATE TABLE `printers_profile` ( Link Here
1379
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
1395
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
1380
1396
1381
--
1397
--
1382
-- Table structure for table `repeatable_holidays`
1383
--
1384
1385
DROP TABLE IF EXISTS `repeatable_holidays`;
1386
CREATE TABLE `repeatable_holidays` ( -- information for the days the library is closed
1387
  `id` int(11) NOT NULL auto_increment, -- unique identifier assigned by Koha
1388
  `branchcode` varchar(10) NOT NULL default '', -- foreign key from the branches table, defines which branch this closing is for
1389
  `weekday` smallint(6) default NULL, -- day of the week (0=Sunday, 1=Monday, etc) this closing is repeated on
1390
  `day` smallint(6) default NULL, -- day of the month this closing is on
1391
  `month` smallint(6) default NULL, -- month this closing is in
1392
  `title` varchar(50) NOT NULL default '', -- title of this closing
1393
  `description` MEDIUMTEXT NOT NULL, -- description for this closing
1394
  PRIMARY KEY  (`id`)
1395
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
1396
1397
--
1398
-- Table structure for table `reports_dictionary`
1398
-- Table structure for table `reports_dictionary`
1399
--
1399
--
1400
1400
Lines 1970-1992 CREATE TABLE `reviews` ( -- patron opac comments Link Here
1970
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
1970
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
1971
1971
1972
--
1972
--
1973
-- Table structure for table `special_holidays`
1974
--
1975
1976
DROP TABLE IF EXISTS `special_holidays`;
1977
CREATE TABLE `special_holidays` ( -- non repeatable holidays/library closings
1978
  `id` int(11) NOT NULL auto_increment, -- unique identifier assigned by Koha
1979
  `branchcode` varchar(10) NOT NULL default '', -- foreign key from the branches table, defines which branch this closing is for
1980
  `day` smallint(6) NOT NULL default 0, -- day of the month this closing is on
1981
  `month` smallint(6) NOT NULL default 0, -- month this closing is in
1982
  `year` smallint(6) NOT NULL default 0, -- year this closing is in
1983
  `isexception` smallint(1) NOT NULL default 1, -- is this a holiday exception to a repeatable holiday (1 for yes, 0 for no)
1984
  `title` varchar(50) NOT NULL default '', -- title for this closing
1985
  `description` MEDIUMTEXT NOT NULL, -- description of this closing
1986
  PRIMARY KEY  (`id`)
1987
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
1988
1989
--
1990
-- Table structure for table `statistics`
1973
-- Table structure for table `statistics`
1991
--
1974
--
1992
1975
(-)a/installer/data/mysql/updatedatabase.pl (-1 / +1 lines)
Lines 14372-14378 if( CheckVersion( $DBversion ) ) { Link Here
14372
14372
14373
        my $expirationdate = dt_from_string($hold->waitingdate);
14373
        my $expirationdate = dt_from_string($hold->waitingdate);
14374
        if ( C4::Context->preference("ExcludeHolidaysFromMaxPickUpDelay") ) {
14374
        if ( C4::Context->preference("ExcludeHolidaysFromMaxPickUpDelay") ) {
14375
            my $calendar = Koha::DiscreteCalendar->new( branchcode => $hold->branchcode );
14375
            my $calendar = Koha::DiscreteCalendar->new({ branchcode => $hold->branchcode });
14376
            $expirationdate = $calendar->days_forward( $expirationdate, $max_pickup_delay );
14376
            $expirationdate = $calendar->days_forward( $expirationdate, $max_pickup_delay );
14377
        } else {
14377
        } else {
14378
            $expirationdate->add( days => $max_pickup_delay );
14378
            $expirationdate->add( days => $max_pickup_delay );
(-)a/koha-tmpl/intranet-tmpl/prog/css/discretecalendar.css (+206 lines)
Line 0 Link Here
1
#jcalendar-container .ui-datepicker {
2
    font-size:185%;
3
}
4
5
#holidayweeklyrepeatable,
6
#holidaysyearlyrepeatable,
7
#holidaysunique,
8
#holidayexceptions {
9
    font-size:90%;
10
    margin-bottom:1em;
11
}
12
13
#showHoliday {
14
    margin:.5em 0;
15
}
16
17
.key {
18
    padding:3px;
19
    white-space:nowrap;
20
    line-height:230%;
21
}
22
23
.ui-datepicker {
24
    font-size:150%;
25
}
26
27
.ui-datepicker th,
28
.ui-datepicker .ui-datepicker-title select {
29
    font-size:80%;
30
}
31
32
.ui-datepicker td a {
33
    padding:.5em;
34
}
35
36
.ui-datepicker td span {
37
    padding:.5em;
38
    border:1px solid #BCBCBC;
39
}
40
41
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
42
    font-size:80%;
43
}
44
45
.key {
46
    padding:3px;
47
    white-space:nowrap;
48
    line-height:230%;
49
}
50
51
.normalday {
52
    background-color:#EDEDED;
53
    color:#000;
54
    border:1px solid #BCBCBC;
55
}
56
57
.ui-datepicker-unselectable {
58
    padding:.5em;
59
    white-space:nowrap;
60
}
61
62
.ui-state-disabled {
63
    padding:.5em;
64
    white-space:nowrap;
65
}
66
67
.exception {
68
    background-color:#b3d4ff;
69
    color:#000;
70
    border:1px solid #BCBCBC;
71
}
72
73
.past-date {
74
    background-color:#e6e6e6;
75
    color:#555;
76
    border:1px solid #BCBCBC;
77
}
78
79
td.past-date a.ui-state-default {
80
    background:#e6e6e6;
81
    color:#555;
82
}
83
84
.float {
85
    background-color:#6f3;
86
    color:#000;
87
    border:1px solid #BCBCBC;
88
}
89
90
.holiday {
91
    background-color:#ffaeae;
92
    color:#000;
93
    border:1px solid #BCBCBC;
94
}
95
96
.repeatableweekly {
97
    background-color:#FF9;
98
    color:#000;
99
    border:1px solid #BCBCBC;
100
}
101
102
.repeatableyearly {
103
    background-color:#FC6;
104
    color:#000;
105
    border:1px solid #BCBCBC;
106
}
107
108
td.exception a.ui-state-default {
109
    background:#b3d4ff none;
110
    color:#000;
111
    border:1px solid #BCBCBC;
112
}
113
114
td.float a.ui-state-default {
115
    background:#6f3 none;
116
    color:#000;
117
    border:1px solid #BCBCBC;
118
}
119
120
td.holiday a.ui-state-default {
121
    background:#ffaeae none;
122
    color:#000;
123
    border:1px solid #BCBCBC;
124
}
125
126
td.repeatableweekly a.ui-state-default {
127
    background:#FF9 none;
128
    color:#000;
129
    border:1px solid #BCBCBC;
130
}
131
132
td.repeatableyearly a.ui-state-default {
133
    background:#FC6 none;
134
    color:#000;
135
    border:1px solid #BCBCBC;
136
}
137
138
.information {
139
    background-color:#DCD2F1;
140
    width:300px;
141
    display:none;
142
    border:1px solid #000;
143
    color:#000;
144
    font-size:8pt;
145
    font-weight:700;
146
    background-color:#FFD700;
147
    cursor:pointer;
148
    padding:2px;
149
}
150
151
.panel {
152
    z-index:1;
153
    display:none;
154
    border:3px solid #CCC;
155
    padding:3px;
156
    margin-top:.3em;
157
    background-color:#FEFEFE;
158
}
159
160
fieldset.brief {
161
    border:0;
162
    margin-top:0;
163
}
164
165
h1 select {
166
    width:20em;
167
}
168
169
div.yui-b fieldset.brief ol {
170
    font-size:100%;
171
}
172
173
div.yui-b fieldset.brief li,
174
div.yui-b fieldset.brief li.radio {
175
    padding:.2em 0;
176
}
177
178
.help {
179
    margin:.3em 0;
180
    border:1px solid #EEE;
181
    padding:.3em .7em;
182
    font-size:90%;
183
}
184
185
.calendar td,
186
.calendar th,
187
.calendar .button,
188
.calendar tbody .day {
189
    padding:.7em;
190
    font-size:110%;
191
}
192
193
.calendar {
194
    width:auto;
195
    border:0;
196
}
197
198
.copyHoliday form li {
199
    display:table-row;
200
}
201
202
.copyHoliday form li b,
203
.copyHoliday form li input {
204
    display:table-cell;
205
    margin-bottom:2px;
206
}
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/discrete_calendar.tt (-504 / +394 lines)
Lines 1-115 Link Here
1
[% USE Branches %]
1
[% USE Branches %]
2
[% SET footerjs = 1 %]
2
[% INCLUDE 'doc-head-open.inc' %]
3
[% INCLUDE 'doc-head-open.inc' %]
3
<title>Koha &rsaquo; Tools &rsaquo; [% Branches.GetName( branch ) %] calendar</title>
4
<title>Koha &rsaquo; Tools &rsaquo; [% Branches.GetName( branch ) %] calendar</title>
4
[% INCLUDE 'doc-head-close.inc' %]
5
[% INCLUDE 'doc-head-close.inc' %]
5
[% INCLUDE 'calendar.inc' %]
6
[% INCLUDE 'calendar.inc' %]
6
<link rel="stylesheet" type="text/css" href="[% interface %]/[% theme %]/css/datatables.css" />
7
<link rel="stylesheet" type="text/css" href="[% interface %]/[% theme %]/css/datatables_[% KOHA_VERSION %].css" />
7
<script type="text/javascript" src="[% interface %]/lib/jquery/plugins/jquery-ui-timepicker-addon.min.js"></script>
8
<link rel="stylesheet" type="text/css" href="[% interface %]/[% theme %]/css/discretecalendar_[% KOHA_VERSION %].css" />
8
[% INCLUDE 'datatables.inc' %]
9
[% INCLUDE 'datatables.inc' %]
9
    <script type="text/javascript">
10
</head>
10
    //<![CDATA[
11
        var weekdays = new Array(_("Sundays"),_("Mondays"),_("Tuesdays"),_("Wednesdays"),_("Thursdays"),_("Fridays"),_("Saturdays"));
12
        // Array containing all the information about each date in the calendar.
13
        var datesInfos = new Array();
14
        [% FOREACH date IN datesInfos %]
15
            datesInfos["[% date.date %]"] = {
16
                title : "[% date.note %]",
17
                outputdate : "[% date.outputdate %]",
18
                holiday_type:"[% date.holiday_type %]",
19
                open_hour: "[% date.open_hour %]",
20
                close_hour: "[% date.close_hour %]"
21
            };
22
        [% END %]
23
11
24
        /**
12
<body id="tools_holidays" class="tools">
25
        * Displays the details of the selected date on a side panel
13
[% INCLUDE 'header.inc' %]
26
        */
14
[% INCLUDE 'cat-search.inc' %]
27
        function showHoliday (date_obj, dateString, dayName, day, month, year, weekDay, title, holidayType) {
28
            $("#newHoliday").slideDown("fast");
29
            $("#copyHoliday").slideUp("fast");
30
            $('#newDaynameOutput').html(dayName);
31
            $('#newDayname').val(dayName);
32
            $('#newBranchNameOutput').html($("#branch :selected").text());
33
            $(".newHoliday ,#branch").val($('#branch').val());
34
            $('#newDayOutput').html(day);
35
            $(".newHoliday #Day").val(day);
36
            $(".newHoliday #Month").val(month);
37
            $(".newHoliday #Year").val(year);
38
            $("#newMonthOutput").html(month);
39
            $("#newYearOutput").html(year);
40
            $(".newHoliday, #Weekday").val(weekDay);
41
15
42
            $('.newHoliday #title').val(title);
16
<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a> &rsaquo; [% Branches.GetName( branch ) %] calendar</div>
43
            $('#HolidayType').val(holidayType);
44
            $('#days_of_week option[value="'+ (weekDay + 1)  +'"]').attr('selected', true);
45
            $('#openHour').val(datesInfos[dateString].open_hour);
46
            $('#closeHour').val(datesInfos[dateString].close_hour);
47
            $('#local_today').val(getSeparetedDate(new Date()).dateString);
48
17
49
            //This changes the label of the date type on the edit panel
18
<div id="doc3" class="yui-t1">
50
            if(holidayType == 'W') {
51
                $("#holtype").attr("class","key repeatableweekly").html(_("Holiday repeating weekly"));
52
            } else if(holidayType == 'R') {
53
                $("#holtype").attr("class","key repeatableyearly").html(_("Holiday repeating yearly"));
54
            } else if(holidayType == 'F') {
55
                $("#holtype").attr("class","key float").html(_("Floating holiday"));
56
            } else if(holidayType == 'N') {
57
                $("#holtype").attr("class","key exception").html(_("Needs validation"));
58
            } else if(holidayType == 'E') {
59
                $("#holtype").attr("class","key holiday").html(_("Unique holiday"));
60
            } else{
61
                $("#holtype").attr("class","key normalday").html(_("Working day "));
62
            }
63
19
64
            //Select the correct holiday type on the dropdown menu
20
   <div id="bd">
65
            if (datesInfos[dateString].holiday_type !=''){
21
    <div id="yui-main">
66
                var type = datesInfos[dateString].holiday_type;
22
    <div class="yui-b">
67
                $('#holidayType option[value="'+ type +'"]').attr('selected', true)
23
    <h2>[% Branches.GetName( branch ) %] calendar</h2>
68
            }else{
24
    <div class="yui-g">
69
                $('#holidayType option[value="none"]').attr('selected', true)
25
    <div class="yui-u first" style="width:60%">
70
            }
26
        <label for="branch">Define the holidays for:</label>
27
        <form method="post" onsubmit="return validateForm('CopyCalendar')">
28
            <select id="branch" name="branch">
29
                [% PROCESS options_for_libraries libraries => Branches.all( selected => branch ) %]
30
            </select>
31
            Copy calendar to
32
            <select id='newBranch' name ='newBranch'>
33
                <option value=""></option>
34
                [% FOREACH l IN Branches.all() %]
35
                    [% UNLESS branch == l.branchcode %]
36
                    <option value="[% l.branchcode %]">[% l.branchname %]</option>
37
                    [% END %]
38
                [% END %]
39
            </select>
40
            <input type="hidden" name="action" value="copyBranch" />
41
            <input type="submit" value="Clone">
42
        </form>
43
            <h3>Calendar information</h3>
44
            <div id="jcalendar-container" style="float: left"></div>
45
    <!-- ***************************** Panel to deal with new holidays **********************  -->
46
    [% UNLESS  datesInfos %]
47
    <div class="alert alert-danger" style="float: left; margin-left:15px">
48
        <strong>Error!</strong> You have to run generate_discrete_calendar.pl in order to use Discrete Calendar.
49
    </div>
50
    [% END %]
71
51
72
            //If it is a weekly or repeatable holiday show the option to delete the type
52
    [% IF  no_branch_selected %]
73
            if(datesInfos[dateString].holiday_type == 'W' || datesInfos[dateString].holiday_type == 'R'){
53
    <div class="alert alert-danger" style="float: left; margin-left:15px">
74
                $('#deleteType').show("fast");
54
        <strong>No library set!</strong> You are using the default calendar.
75
            }else{
55
    </div>
76
                $('#deleteType').hide("fast");
56
    [% END %]
77
            }
78
57
79
            //This value is to disable and hide input when the date is in the past, because you can't edit it.
58
    <div class="panel newHoliday" id="newHoliday" style="float: left; margin-left:15px">
80
            var value = false;
59
        <form method="post" onsubmit="return validateForm('newHoliday')">
81
            var today = new Date();
60
            <fieldset class="brief">
82
            today.setHours(0,0,0,0);
61
                <h3>Edit date details</h3>
83
            if(date_obj < today ){
62
                <span id="holtype"></span>
84
                $("#holtype").attr("class","key past-date").html(_("Past date"));
63
                <ol>
85
                $("#CopyRadioButton").attr("checked", "checked");
64
                    <li>
86
                value = true;
65
                        <strong>Library:</strong>
87
                $(".CopyDatePanel").toggle(value);
66
                        <span id="newBranchNameOutput"></span>
88
            }
67
                        <input type="hidden" id="branch" name="branch" />
89
            $("#title").prop('disabled', value);
68
                    </li>
90
            $("#holidayType select").prop('disabled', value);
69
                    <li>
91
            $("#openHour").prop('disabled', value);
70
                        <strong>From date:</strong>
92
            $("#closeHour").prop('disabled', value);
71
                        <span id="newDaynameOutput"></span>,
93
            $("#EditRadioButton").parent().toggle(!value);
94
72
95
        }
73
                        [% 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 %]
96
74
97
        function hidePanel(aPanelName) {
75
                        <input type="hidden" id="newDayname" name="showDayname" />
98
            $("#"+aPanelName).slideUp("fast");
76
                        <input type="hidden" id="Day" name="Day" />
99
        }
77
                        <input type="hidden" id="Month" name="Month" />
78
                        <input type="hidden" id="Year" name="Year" />
79
                    </li>
80
                    <li class="dateinsert">
81
                        <b>To date: </b>
82
                        <input type="text" id="from_copyToDatePicker" name="toDate" size="20" class="datepicker" />
83
                    </li>
84
                    <li>
85
                        <label for="title">Title: </label><input type="text" name="Title" id="title" size="35" />
86
                    </li>
87
                    <li id="holidayType">
88
                        <label for="holidayType">Date type</label>
89
                        <select name ='holidayType'>
90
                            <option value="empty"></option>
91
                            <option value="none">Working day</option>
92
                            <option value="E">Unique holiday</option>
93
                            <option value="W">Weekly holiday</option>
94
                            <option value="R">Repeatable holiday</option>
95
                            <option value="F">Floating holiday</option>
96
                            <option value="N" disabled>Need validation</option>
97
                        </select>
98
                    </li>
99
                    <li id="days_of_week" style="display :none">
100
                        <label for="day_of_week">Week day</label>
101
                        <select name ='day_of_week'>
102
                            <option value="everyday">Everyday</option>
103
                            <option value="1">Sundays</option>
104
                            <option value="2">Mondays</option>
105
                            <option value="3">Tuesdays</option>
106
                            <option value="4">Wednesdays</option>
107
                            <option value="5">Thursdays</option>
108
                            <option value="6">Fridays</option>
109
                            <option value="7">Saturdays</option>
110
                        </select>
111
                    </li>
112
                    <li class="radio" id="deleteType" style="display : none;" >
113
                        <input type="checkbox" name="deleteType" id="deleteType_checkbox" value="1" ><label for="deleteType_checkbox"> Delete this type</label>
114
                        <a href="#" class="helptext">[?]</a>
115
                        <div class="hint">Remove all repeated or weekly holidays of the selected date or week day <br> if working day is selected.</div>
116
                    </li>
117
                    <li>
118
                        <label for="openHour">Open hours: </label><input type="text" name="openHour" id='openHour' style="display :flex"  >
119
                    </li>
120
                    <li>
121
                        <label for="closeHour">Close hours: </label><input type="text" name="closeHour" id='closeHour' style="display :flex" >
122
                    </li>
123
                    <li class="radio">
124
                        <input type="radio" name="action" id="EditRadioButton" value="edit" checked/>
125
                        <label for="EditRadioButton">Edit selected dates</label>
126
                    </li>
127
                    <li class="radio">
128
                        <input type="radio" name="action" id="CopyRadioButton" value="copyDates" />
129
                        <label for="CopyRadioButton">Copy to different dates</label>
130
                        <div class="CopyDatePanel" style="display:none; padding-left:15px">
131
                            <b>From : </b>
132
                            <input type="text" id="to_copyFromDatePicker" size="20" class="datepicker"/>
133
                            <b>To : </b>
134
                            <input type="text" id="to_copyToDatePicker" size="20" class="datepicker"/>
135
                        </div>
136
                        <input type="hidden" name="daysnumber" id='daysnumber'>
137
                        <!-- These  yyyy-mm-dd -->
138
                        <input type="hidden" name="from_copyFrom" id='from_copyFrom'>
139
                        <input type="hidden" name="from_copyTo" id='from_copyTo'>
140
                        <input type="hidden" name="to_copyFrom" id='to_copyFrom'>
141
                        <input type="hidden" name="to_copyTo" id='to_copyTo'>
142
                        <input type="hidden" name="local_today" id='local_today'>
143
                    </li>
144
                </ol>
145
                <fieldset class="action">
146
                    <input type="submit" name="submit" value="Save" />
147
                    <a href="#" class="cancel hidePanel newHoliday">Cancel</a>
148
                </fieldset>
149
            </fieldset>
150
        </form>
151
    </div>
100
152
101
        function changeBranch () {
153
<!-- ************************************************************************************** -->
102
            var branch = $("#branch option:selected").val();
154
<!-- ******                              MAIN SCREEN CODE                            ****** -->
103
            location.href='/cgi-bin/koha/tools/discrete_calendar.pl?branch=' + branch + '&calendardate=' + "[% calendardate %]";
155
<!-- ************************************************************************************** -->
104
        }
105
156
106
        function Help() {
157
</div>
107
            newin=window.open("/cgi-bin/koha/help.pl","KohaHelp",'width=600,height=600,toolbar=false,scrollbars=yes');
158
<div class="yui-u" style="width : 40%">
108
        }
159
    <div class="help">
160
        <h4>Hints</h4>
161
        <ul>
162
            <li>Search in the calendar the day you want to set as holiday.</li>
163
            <li>Click the date to add or edit a holiday.</li>
164
            <li>Specify how the holiday should repeat.</li>
165
            <li>Click Save to finish.</li>
166
            <li>PS:
167
                <ul>
168
                    <li>You can't edit passed dates</li>
169
                    <li>Weekly holidays change open/close hours for all the days affected unless inputs are empty</li>
170
                </ul>
171
            </li>
172
        </ul>
173
        <h4>Key</h4>
174
        <p>
175
            <span class="key normalday">Working day </span>
176
            <span class="key holiday">Unique holiday</span>
177
            <span class="key repeatableweekly">Holiday repeating weekly</span>
178
            <span class="key repeatableyearly">Holiday repeating yearly</span>
179
            <span class="key float">Floating holiday</span>
180
            <span class="key exception">Need validation</span>
181
        </p>
182
    </div>
183
<div id="holiday-list">
109
184
110
        // This function gives css clases to each kind of day
185
    [% IF ( NEED_VALIDATION_HOLIDAYS ) %]
111
        function dateStatusHandler(date) {
186
    <h3>Need validation holidays</h3>
112
            date = getSeparetedDate(date);
187
    <table id="holidaysunique">
188
        <thead>
189
            <tr>
190
                <th class="exception">Date</th>
191
                <th class="exception">Title</th>
192
            </tr>
193
        </thead>
194
        <tbody>
195
            [% FOREACH need_validation_holiday IN NEED_VALIDATION_HOLIDAYS %]
196
            <tr>
197
                <td><a href="#doc3" onclick="go_to_date('[% need_validation_holiday.date %]')"><span title="[% need_validation_holiday.DATE_SORT %]">[% need_validation_holiday.outputdate %]</span></a></td>
198
                <td>[% need_validation_holiday.note %]</td>
199
            </tr>
200
            [% END %]
201
        </tbody>
202
    </table>
203
    [% END %]
204
205
    [% IF ( WEEKLY_HOLIDAYS ) %]
206
    <h3>Weekly - Repeatable holidays</h3>
207
    <table id="holidayweeklyrepeatable">
208
        <thead>
209
            <tr>
210
                <th class="repeatableweekly">Day of week</th>
211
                <th class="repeatableweekly">Title</th>
212
            </tr>
213
        </thead>
214
        <tbody>
215
            [% FOREACH WEEK_DAYS_LOO IN WEEKLY_HOLIDAYS %]
216
            <tr>
217
                <td>[% WEEK_DAYS_LOO.weekday %]</td>
218
            </td>
219
            <td>[% WEEK_DAYS_LOO.note %]</td>
220
        </tr>
221
        [% END %]
222
    </tbody>
223
</table>
224
[% END %]
225
226
[% IF ( REPEATABLE_HOLIDAYS ) %]
227
<h3>Yearly - Repeatable holidays</h3>
228
<table id="holidaysyearlyrepeatable">
229
    <thead>
230
        <tr>
231
            [% IF ( dateformat == "metric" ) %]
232
            <th class="repeatableyearly">Day/month</th>
233
            [% ELSE %]
234
            <th class="repeatableyearly">Month/day</th>
235
            [% END %]
236
            <th class="repeatableyearly">Title</th>
237
        </tr>
238
    </thead>
239
    <tbody>
240
        [% FOREACH DAY_MONTH_HOLIDAYS_LOO IN REPEATABLE_HOLIDAYS %]
241
        <tr>
242
            [% IF ( dateformat == "metric" ) %]
243
            <td><span title="[% DAY_MONTH_HOLIDAYS_LOO.DATE_SORT %]">[% DAY_MONTH_HOLIDAYS_LOO.day %]/[% DAY_MONTH_HOLIDAYS_LOO.month %]</span></td>
244
            [% ELSE %]
245
            <td><span title="[% DAY_MONTH_HOLIDAYS_LOO.DATE_SORT %]">[% DAY_MONTH_HOLIDAYS_LOO.month %]/[% DAY_MONTH_HOLIDAYS_LOO.day %]</span></td>
246
            [% END %]
247
            <td>[% DAY_MONTH_HOLIDAYS_LOO.note %]</td>
248
        </tr>
249
        [% END %]
250
    </tbody>
251
</table>
252
[% END %]
253
254
[% IF ( UNIQUE_HOLIDAYS ) %]
255
<h3>Unique holidays</h3>
256
<table id="holidaysunique">
257
    <thead>
258
        <tr>
259
            <th class="holiday">Date</th>
260
            <th class="holiday">Title</th>
261
        </tr>
262
    </thead>
263
    <tbody>
264
        [% FOREACH HOLIDAYS_LOO IN UNIQUE_HOLIDAYS %]
265
        <tr>
266
            <td><a href="#doc3" onclick="go_to_date('[% HOLIDAYS_LOO.date %]')"><span title="[% HOLIDAYS_LOO.DATE_SORT %]">[% HOLIDAYS_LOO.outputdate %]</span></a></td>
267
            <td>[% HOLIDAYS_LOO.note %]</td>
268
        </tr>
269
        [% END %]
270
    </tbody>
271
</table>
272
[% END %]
273
274
[% IF ( FLOAT_HOLIDAYS ) %]
275
<h3>Floating holidays</h3>
276
<table id="holidaysunique">
277
    <thead>
278
        <tr>
279
            <th class="float">Date</th>
280
            <th class="float">Title</th>
281
        </tr>
282
    </thead>
283
    <tbody>
284
        [% FOREACH float_holiday IN FLOAT_HOLIDAYS %]
285
        <tr>
286
            <td><a href="#doc3" onclick="go_to_date('[% float_holiday.date %]')"><span title="[% float_holiday.DATE_SORT %]">[% float_holiday.outputdate %]</span></a></td>
287
            <td>[% float_holiday.note %]</td>
288
        </tr>
289
        [% END %]
290
    </tbody>
291
</table>
292
[% END %]
293
</div>
294
</div>
295
</div>
296
</div>
297
</div>
298
299
<div class="yui-b noprint">
300
[% INCLUDE 'tools-menu.inc' %]
301
</div>
302
</div>
303
[% MACRO jsinclude BLOCK %]
304
<script type="text/javascript" src="[% interface %]/lib/jquery/plugins/jquery-ui-timepicker-addon.min_[% KOHA_VERSION %].js"></script>
305
<script type="text/javascript">
306
    //<![CDATA[
307
        var weekdays = new Array(_("Sundays"),_("Mondays"),_("Tuesdays"),_("Wednesdays"),_("Thursdays"),_("Fridays"),_("Saturdays"));
308
        // Array containing all the information about each date in the calendar.
309
        var datesInfos = new Array();
310
        [% FOREACH date IN datesInfos %]
311
            datesInfos["[% date.date %]"] = {
312
                title : "[% date.note %]",
313
                outputdate : "[% date.outputdate %]",
314
                holiday_type:"[% date.holiday_type %]",
315
                open_hour: "[% date.open_hour %]",
316
                close_hour: "[% date.close_hour %]"
317
            };
318
        [% END %]
319
320
        /**
321
        * Displays the details of the selected date on a side panel
322
        */
323
        function showHoliday (date_obj, dateString, dayName, day, month, year, weekDay, title, holidayType) {
324
            $("#newHoliday").slideDown("fast");
325
            $("#copyHoliday").slideUp("fast");
326
            $('#newDaynameOutput').html(dayName);
327
            $('#newDayname').val(dayName);
328
            $('#newBranchNameOutput').html($("#branch :selected").text());
329
            $(".newHoliday ,#branch").val($('#branch').val());
330
            $('#newDayOutput').html(day);
331
            $(".newHoliday #Day").val(day);
332
            $(".newHoliday #Month").val(month);
333
            $(".newHoliday #Year").val(year);
334
            $("#newMonthOutput").html(month);
335
            $("#newYearOutput").html(year);
336
            $(".newHoliday, #Weekday").val(weekDay);
337
338
            $('.newHoliday #title').val(title);
339
            $('#HolidayType').val(holidayType);
340
            $('#days_of_week option[value="'+ (weekDay + 1)  +'"]').attr('selected', true);
341
            $('#openHour').val(datesInfos[dateString].open_hour);
342
            $('#closeHour').val(datesInfos[dateString].close_hour);
343
            $('#local_today').val(getSeparetedDate(new Date()).dateString);
344
345
            //This changes the label of the date type on the edit panel
346
            if(holidayType == 'W') {
347
                $("#holtype").attr("class","key repeatableweekly").html(_("Holiday repeating weekly"));
348
            } else if(holidayType == 'R') {
349
                $("#holtype").attr("class","key repeatableyearly").html(_("Holiday repeating yearly"));
350
            } else if(holidayType == 'F') {
351
                $("#holtype").attr("class","key float").html(_("Floating holiday"));
352
            } else if(holidayType == 'N') {
353
                $("#holtype").attr("class","key exception").html(_("Needs validation"));
354
            } else if(holidayType == 'E') {
355
                $("#holtype").attr("class","key holiday").html(_("Unique holiday"));
356
            } else{
357
                $("#holtype").attr("class","key normalday").html(_("Working day "));
358
            }
359
360
            //Select the correct holiday type on the dropdown menu
361
            if (datesInfos[dateString].holiday_type !=''){
362
                var type = datesInfos[dateString].holiday_type;
363
                $('#holidayType option[value="'+ type +'"]').attr('selected', true)
364
            }else{
365
                $('#holidayType option[value="none"]').attr('selected', true)
366
            }
367
368
            //If it is a weekly or repeatable holiday show the option to delete the type
369
            if(datesInfos[dateString].holiday_type == 'W' || datesInfos[dateString].holiday_type == 'R'){
370
                $('#deleteType').show("fast");
371
            }else{
372
                $('#deleteType').hide("fast");
373
            }
374
375
            //This value is to disable and hide input when the date is in the past, because you can't edit it.
376
            var value = false;
377
            var today = new Date();
378
            today.setHours(0,0,0,0);
379
            if(date_obj < today ){
380
                $("#holtype").attr("class","key past-date").html(_("Past date"));
381
                $("#CopyRadioButton").attr("checked", "checked");
382
                value = true;
383
                $(".CopyDatePanel").toggle(value);
384
            }
385
            $("#title").prop('disabled', value);
386
            $("#holidayType select").prop('disabled', value);
387
            $("#openHour").prop('disabled', value);
388
            $("#closeHour").prop('disabled', value);
389
            $("#EditRadioButton").parent().toggle(!value);
390
391
        }
392
393
        function hidePanel(aPanelName) {
394
            $("#"+aPanelName).slideUp("fast");
395
        }
396
397
        function changeBranch () {
398
            var branch = $("#branch option:selected").val();
399
            location.href='/cgi-bin/koha/tools/discrete_calendar.pl?branch=' + branch + '&calendardate=' + "[% calendardate %]";
400
        }
401
402
        function Help() {
403
            newin=window.open("/cgi-bin/koha/help.pl","KohaHelp",'width=600,height=600,toolbar=false,scrollbars=yes');
404
        }
405
406
        // This function gives css clases to each kind of day
407
        function dateStatusHandler(date) {
408
            date = getSeparetedDate(date);
113
            var day = date.day;
409
            var day = date.day;
114
            var month = date.month;
410
            var month = date.month;
115
            var year = date.year;
411
            var year = date.year;
Lines 297-303 Link Here
297
                },
593
                },
298
                defaultDate: new Date("[% keydate %]"),
594
                defaultDate: new Date("[% keydate %]"),
299
                minDate: new Date("[% minDate %]"),
595
                minDate: new Date("[% minDate %]"),
300
                maxDate: new Date("[% maxDate %]")
596
                maxDate: new Date("[% maxDate %]"),
597
                dateFormat: "yy-mm-dd"
301
            });
598
            });
302
            //Main datepicker
599
            //Main datepicker
303
            $("#jcalendar-container").datepicker({
600
            $("#jcalendar-container").datepicker({
Lines 375-787 Link Here
375
            });
672
            });
376
        });
673
        });
377
    //]]>
674
    //]]>
378
    </script>
675
</script>
379
    <!-- Datepicker colors -->
380
    <style type="text/css">
381
        #jcalendar-container .ui-datepicker {
382
            font-size : 185%;
383
        }
384
        #holidayweeklyrepeatable, #holidaysyearlyrepeatable, #holidaysunique, #holidayexceptions {
385
            font-size : 90%; margin-bottom : 1em;
386
        }
387
        #showHoliday {
388
            margin : .5em 0;
389
        }
390
        .key {
391
            padding : 3px;
392
            white-space:nowrap;
393
            line-height:230%;
394
        }
395
        .ui-datepicker {
396
            font-size : 150%;
397
        }
398
        .ui-datepicker th, .ui-datepicker .ui-datepicker-title select {
399
            font-size : 80%;
400
        }
401
        .ui-datepicker td a {
402
            padding : .5em;
403
        }
404
        .ui-datepicker td span {
405
            padding : .5em;
406
            border : 1px solid #BCBCBC;
407
        }
408
        .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
409
            font-size : 80%;
410
        }
411
        .key {
412
            padding : 3px; white-space:nowrap; line-height:230%;
413
        }
414
        .normalday {
415
            background-color :  #EDEDED;
416
            color :  Black;
417
            border : 1px solid #BCBCBC;
418
        }
419
        .ui-datepicker-unselectable {
420
            padding :.5em; white-space:nowrap;
421
        }
422
        .ui-state-disabled {
423
            padding :.5em; white-space:nowrap;
424
        }
425
        .exception {
426
            background-color :  #b3d4ff; color :  Black; border : 1px solid #BCBCBC;
427
        }
428
        .past-date {
429
            background-color :  #e6e6e6; color :  #555555; border : 1px solid #BCBCBC;
430
        }
431
        td.past-date a.ui-state-default {
432
            background : #e6e6e6 ; color :  #555555;
433
        }
434
        .float {
435
            background-color :  #66ff33; color :  Black; border : 1px solid #BCBCBC;
436
        }
437
        .holiday {
438
            background-color :  #ffaeae; color :  Black;  border : 1px solid #BCBCBC;
439
        }
440
        .repeatableweekly {
441
            background-color :  #FFFF99; color :  Black;  border : 1px solid #BCBCBC;
442
        }
443
        .repeatableyearly {
444
            background-color :  #FFCC66; color :  Black;  border : 1px solid #BCBCBC;
445
        }
446
        td.exception a.ui-state-default {
447
            background:  #b3d4ff none; color :  Black; border : 1px solid #BCBCBC;
448
        }
449
        td.float a.ui-state-default {
450
            background:  #66ff33 none; color :  Black; border : 1px solid #BCBCBC;
451
        }
452
        td.holiday a.ui-state-default {
453
            background:  #ffaeae none; color :  Black;  border : 1px solid #BCBCBC;
454
        }
455
        td.repeatableweekly a.ui-state-default {
456
            background:  #FFFF99 none; color :  Black;  border : 1px solid #BCBCBC;
457
        }
458
        td.repeatableyearly a.ui-state-default {
459
            background:  #FFCC66 none; color :  Black;  border : 1px solid #BCBCBC;
460
        }
461
        .information {
462
            z-index : ; background-color :  #DCD2F1; width : 300px; display : none; border : 1px solid #000000; color :  #000000; font-size :  8pt; font-weight :  bold; background-color :  #FFD700; cursor :  pointer; padding : 2px;
463
        }
464
        .panel {
465
            z-index : 1; display : none; border : 3px solid #CCC; padding : 3px; margin-top: .3em;  background-color: #FEFEFE;
466
        }
467
        fieldset.brief {
468
            border : 0; margin-top: 0;
469
        }
470
        h1 select {
471
            width: 20em;
472
        }
473
        div.yui-b fieldset.brief ol {
474
            font-size:100%;
475
        }
476
        div.yui-b fieldset.brief li, div.yui-b fieldset.brief li.radio  {
477
            padding:0.2em 0;
478
        }
479
        .help {
480
            margin:.3em 0;border:1px solid #EEE;padding:.3em .7em; font-size : 90%;
481
        }
482
        .calendar td, .calendar th, .calendar .button, .calendar tbody .day {
483
            padding : .7em; font-size: 110%;
484
        }
485
        .calendar { width: auto; border : 0;
486
        }
487
        .copyHoliday form li{
488
            display:table-row
489
        }
490
        .copyHoliday form li b, .copyHoliday form li input{
491
            display:table-cell; margin-bottom: 2px;
492
        }
493
    </style>
494
</head>
495
496
<body id="tools_holidays" class="tools">
497
[% INCLUDE 'header.inc' %]
498
[% INCLUDE 'cat-search.inc' %]
499
500
<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a> &rsaquo; [% Branches.GetName( branch ) %] calendar</div>
501
502
<div id="doc3" class="yui-t1">
503
504
   <div id="bd">
505
    <div id="yui-main">
506
    <div class="yui-b">
507
    <h2>[% Branches.GetName( branch ) %] calendar</h2>
508
    <div class="yui-g">
509
    <div class="yui-u first" style="width:60%">
510
        <label for="branch">Define the holidays for:</label>
511
        <form method="post" onsubmit="return validateForm('CopyCalendar')">
512
            <select id="branch" name="branch">
513
                [% PROCESS options_for_libraries libraries => Branches.all( selected => branch ) %]
514
            </select>
515
            Copy calendar to
516
            <select id='newBranch' name ='newBranch'>
517
                <option value=""></option>
518
                [% FOREACH l IN Branches.all() %]
519
                    [% UNLESS branch == l.branchcode %]
520
                    <option value="[% l.branchcode %]">[% l.branchname %]</option>
521
                    [% END %]
522
                [% END %]
523
            </select>
524
            <input type="hidden" name="action" value="copyBranch" />
525
            <input type="submit" value="Clone">
526
        </form>
527
            <h3>Calendar information</h3>
528
            <div id="jcalendar-container" style="float: left"></div>
529
    <!-- ***************************** Panel to deal with new holidays **********************  -->
530
    [% UNLESS  datesInfos %]
531
    <div class="alert alert-danger" style="float: left; margin-left:15px">
532
        <strong>Error!</strong> You have to run generate_discrete_calendar.pl in order to use Discrete Calendar.
533
    </div>
534
    [% END %]
535
536
    [% IF  no_branch_selected %]
537
    <div class="alert alert-danger" style="float: left; margin-left:15px">
538
        <strong>No library set!</strong> You are using the default calendar.
539
    </div>
540
    [% END %]
541
542
    <div class="panel newHoliday" id="newHoliday" style="float: left; margin-left:15px">
543
        <form method="post" onsubmit="return validateForm('newHoliday')">
544
            <fieldset class="brief">
545
                <h3>Edit date details</h3>
546
                <span id="holtype"></span>
547
                <ol>
548
                    <li>
549
                        <strong>Library:</strong>
550
                        <span id="newBranchNameOutput"></span>
551
                        <input type="hidden" id="branch" name="branch" />
552
                    </li>
553
                    <li>
554
                        <strong>From date:</strong>
555
                        <span id="newDaynameOutput"></span>,
556
557
                        [% 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 %]
558
559
                        <input type="hidden" id="newDayname" name="showDayname" />
560
                        <input type="hidden" id="Day" name="Day" />
561
                        <input type="hidden" id="Month" name="Month" />
562
                        <input type="hidden" id="Year" name="Year" />
563
                    </li>
564
                    <li class="dateinsert">
565
                        <b>To date: </b>
566
                        <input type="text" id="from_copyToDatePicker" name="toDate" size="20" class="datepicker" />
567
                    </li>
568
                    <li>
569
                        <label for="title">Title: </label><input type="text" name="Title" id="title" size="35" />
570
                    </li>
571
                    <li id="holidayType">
572
                        <label for="holidayType">Date type</label>
573
                        <select name ='holidayType'>
574
                            <option value="empty"></option>
575
                            <option value="none">Working day</option>
576
                            <option value="E">Unique holiday</option>
577
                            <option value="W">Weekly holiday</option>
578
                            <option value="R">Repeatable holiday</option>
579
                            <option value="F">Floating holiday</option>
580
                            <option value="N" disabled>Need validation</option>
581
                        </select>
582
                    </li>
583
                    <li id="days_of_week" style="display :none">
584
                        <label for="day_of_week">Week day</label>
585
                        <select name ='day_of_week'>
586
                            <option value="everyday">Everyday</option>
587
                            <option value="1">Sundays</option>
588
                            <option value="2">Mondays</option>
589
                            <option value="3">Tuesdays</option>
590
                            <option value="4">Wednesdays</option>
591
                            <option value="5">Thursdays</option>
592
                            <option value="6">Fridays</option>
593
                            <option value="7">Saturdays</option>
594
                        </select>
595
                    </li>
596
                    <li class="radio" id="deleteType" style="display : none;" >
597
                        <input type="checkbox" name="deleteType" id="deleteType_checkbox" value="1" ><label for="deleteType_checkbox"> Delete this type</label>
598
                        <a href="#" class="helptext">[?]</a>
599
                        <div class="hint">Remove all repeated or weekly holidays of the selected date or week day <br> if working day is selected.</div>
600
                    </li>
601
                    <li>
602
                        <label for="openHour">Open hours: </label><input type="text" name="openHour" id='openHour' style="display :flex"  >
603
                    </li>
604
                    <li>
605
                        <label for="closeHour">Close hours: </label><input type="text" name="closeHour" id='closeHour' style="display :flex" >
606
                    </li>
607
                    <li class="radio">
608
                        <input type="radio" name="action" id="EditRadioButton" value="edit" checked/>
609
                        <label for="EditRadioButton">Edit selected dates</label>
610
                    </li>
611
                    <li class="radio">
612
                        <input type="radio" name="action" id="CopyRadioButton" value="copyDates" />
613
                        <label for="CopyRadioButton">Copy to different dates</label>
614
                        <div class="CopyDatePanel" style="display:none; padding-left:15px">
615
                            <b>From : </b>
616
                            <input type="text" id="to_copyFromDatePicker" size="20" class="datepicker"/>
617
                            <b>To : </b>
618
                            <input type="text" id="to_copyToDatePicker" size="20" class="datepicker"/>
619
                        </div>
620
                        <input type="hidden" name="daysnumber" id='daysnumber'>
621
                        <!-- These  yyyy-mm-dd -->
622
                        <input type="hidden" name="from_copyFrom" id='from_copyFrom'>
623
                        <input type="hidden" name="from_copyTo" id='from_copyTo'>
624
                        <input type="hidden" name="to_copyFrom" id='to_copyFrom'>
625
                        <input type="hidden" name="to_copyTo" id='to_copyTo'>
626
                        <input type="hidden" name="local_today" id='local_today'>
627
                    </li>
628
                </ol>
629
                <fieldset class="action">
630
                    <input type="submit" name="submit" value="Save" />
631
                    <a href="#" class="cancel hidePanel newHoliday">Cancel</a>
632
                </fieldset>
633
            </fieldset>
634
        </form>
635
    </div>
636
637
<!-- ************************************************************************************** -->
638
<!-- ******                              MAIN SCREEN CODE                            ****** -->
639
<!-- ************************************************************************************** -->
640
641
</div>
642
<div class="yui-u" style="width : 40%">
643
    <div class="help">
644
        <h4>Hints</h4>
645
        <ul>
646
            <li>Search in the calendar the day you want to set as holiday.</li>
647
            <li>Click the date to add or edit a holiday.</li>
648
            <li>Specify how the holiday should repeat.</li>
649
            <li>Click Save to finish.</li>
650
            <li>PS:
651
                <ul>
652
                    <li>You can't edit passed dates</li>
653
                    <li>Weekly holidays change open/close hours for all the days affected unless inputs are empty</li>
654
                </ul>
655
            </li>
656
        </ul>
657
        <h4>Key</h4>
658
        <p>
659
            <span class="key normalday">Working day </span>
660
            <span class="key holiday">Unique holiday</span>
661
            <span class="key repeatableweekly">Holiday repeating weekly</span>
662
            <span class="key repeatableyearly">Holiday repeating yearly</span>
663
            <span class="key float">Floating holiday</span>
664
            <span class="key exception">Need validation</span>
665
        </p>
666
    </div>
667
<div id="holiday-list">
668
669
    [% IF ( NEED_VALIDATION_HOLIDAYS ) %]
670
    <h3>Need validation holidays</h3>
671
    <table id="holidaysunique">
672
        <thead>
673
            <tr>
674
                <th class="exception">Date</th>
675
                <th class="exception">Title</th>
676
            </tr>
677
        </thead>
678
        <tbody>
679
            [% FOREACH need_validation_holiday IN NEED_VALIDATION_HOLIDAYS %]
680
            <tr>
681
                <td><a href="#doc3" onclick="go_to_date('[% need_validation_holiday.date %]')"><span title="[% need_validation_holiday.DATE_SORT %]">[% need_validation_holiday.outputdate %]</span></a></td>
682
                <td>[% need_validation_holiday.note %]</td>
683
            </tr>
684
            [% END %]
685
        </tbody>
686
    </table>
687
    [% END %]
688
689
    [% IF ( WEEKLY_HOLIDAYS ) %]
690
    <h3>Weekly - Repeatable holidays</h3>
691
    <table id="holidayweeklyrepeatable">
692
        <thead>
693
            <tr>
694
                <th class="repeatableweekly">Day of week</th>
695
                <th class="repeatableweekly">Title</th>
696
            </tr>
697
        </thead>
698
        <tbody>
699
            [% FOREACH WEEK_DAYS_LOO IN WEEKLY_HOLIDAYS %]
700
            <tr>
701
                <td>[% WEEK_DAYS_LOO.weekday %]</td>
702
            </td>
703
            <td>[% WEEK_DAYS_LOO.note %]</td>
704
        </tr>
705
        [% END %]
706
    </tbody>
707
</table>
708
[% END %]
709
710
[% IF ( REPEATABLE_HOLIDAYS ) %]
711
<h3>Yearly - Repeatable holidays</h3>
712
<table id="holidaysyearlyrepeatable">
713
    <thead>
714
        <tr>
715
            [% IF ( dateformat == "metric" ) %]
716
            <th class="repeatableyearly">Day/month</th>
717
            [% ELSE %]
718
            <th class="repeatableyearly">Month/day</th>
719
            [% END %]
720
            <th class="repeatableyearly">Title</th>
721
        </tr>
722
    </thead>
723
    <tbody>
724
        [% FOREACH DAY_MONTH_HOLIDAYS_LOO IN REPEATABLE_HOLIDAYS %]
725
        <tr>
726
            [% IF ( dateformat == "metric" ) %]
727
            <td><span title="[% DAY_MONTH_HOLIDAYS_LOO.DATE_SORT %]">[% DAY_MONTH_HOLIDAYS_LOO.day %]/[% DAY_MONTH_HOLIDAYS_LOO.month %]</span></td>
728
            [% ELSE %]
729
            <td><span title="[% DAY_MONTH_HOLIDAYS_LOO.DATE_SORT %]">[% DAY_MONTH_HOLIDAYS_LOO.month %]/[% DAY_MONTH_HOLIDAYS_LOO.day %]</span></td>
730
            [% END %]
731
            <td>[% DAY_MONTH_HOLIDAYS_LOO.note %]</td>
732
        </tr>
733
        [% END %]
734
    </tbody>
735
</table>
736
[% END %]
676
[% END %]
737
738
[% IF ( UNIQUE_HOLIDAYS ) %]
739
<h3>Unique holidays</h3>
740
<table id="holidaysunique">
741
    <thead>
742
        <tr>
743
            <th class="holiday">Date</th>
744
            <th class="holiday">Title</th>
745
        </tr>
746
    </thead>
747
    <tbody>
748
        [% FOREACH HOLIDAYS_LOO IN UNIQUE_HOLIDAYS %]
749
        <tr>
750
            <td><a href="#doc3" onclick="go_to_date('[% HOLIDAYS_LOO.date %]')"><span title="[% HOLIDAYS_LOO.DATE_SORT %]">[% HOLIDAYS_LOO.outputdate %]</span></a></td>
751
            <td>[% HOLIDAYS_LOO.note %]</td>
752
        </tr>
753
        [% END %]
754
    </tbody>
755
</table>
756
[% END %]
757
758
[% IF ( FLOAT_HOLIDAYS ) %]
759
<h3>Floating holidays</h3>
760
<table id="holidaysunique">
761
    <thead>
762
        <tr>
763
            <th class="float">Date</th>
764
            <th class="float">Title</th>
765
        </tr>
766
    </thead>
767
    <tbody>
768
        [% FOREACH float_holiday IN FLOAT_HOLIDAYS %]
769
        <tr>
770
            <td><a href="#doc3" onclick="go_to_date('[% float_holiday.date %]')"><span title="[% float_holiday.DATE_SORT %]">[% float_holiday.outputdate %]</span></a></td>
771
            <td>[% float_holiday.note %]</td>
772
        </tr>
773
        [% END %]
774
    </tbody>
775
</table>
776
[% END %]
777
</div>
778
</div>
779
</div>
780
</div>
781
</div>
782
783
<div class="yui-b noprint">
784
[% INCLUDE 'tools-menu.inc' %]
785
</div>
786
</div>
787
[% INCLUDE 'intranet-bottom.inc' %]
677
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/misc/cronjobs/add_days_discrete_calendar.pl (-2 / +1 lines)
Lines 3-10 Link Here
3
#
3
#
4
#   This script adds one day into discrete_calendar table based on the same day from the week before
4
#   This script adds one day into discrete_calendar table based on the same day from the week before
5
#
5
#
6
use strict;
6
use Modern::Perl;
7
use warnings;
8
use DateTime;
7
use DateTime;
9
use DateTime::Format::Strptime;
8
use DateTime::Format::Strptime;
10
use Data::Dumper;
9
use Data::Dumper;
(-)a/misc/cronjobs/fines.pl (-1 / +1 lines)
Lines 176-182 EOM Link Here
176
sub set_holiday {
176
sub set_holiday {
177
    my ( $branch, $dt ) = @_;
177
    my ( $branch, $dt ) = @_;
178
178
179
    my $calendar = Koha::DiscreteCalendar->new( branchcode => $branch );
179
    my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branch });
180
    return $calendar->is_holiday($dt);
180
    return $calendar->is_holiday($dt);
181
}
181
}
182
182
(-)a/misc/cronjobs/overdue_notices.pl (-3 / +3 lines)
Lines 445-451 elsif ( defined $text_filename ) { Link Here
445
445
446
foreach my $branchcode (@branches) {
446
foreach my $branchcode (@branches) {
447
    if ( C4::Context->preference('OverdueNoticeCalendar') ) {
447
    if ( C4::Context->preference('OverdueNoticeCalendar') ) {
448
        my $calendar = Koha::DiscreteCalendar->new( branchcode => $branchcode );
448
        my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branchcode });
449
        if ( $calendar->is_holiday($date_to_run) ) {
449
        if ( $calendar->is_holiday($date_to_run) ) {
450
            next;
450
            next;
451
        }
451
        }
Lines 546-552 END_SQL Link Here
546
                my $days_between;
546
                my $days_between;
547
                if ( C4::Context->preference('OverdueNoticeCalendar') )
547
                if ( C4::Context->preference('OverdueNoticeCalendar') )
548
                {
548
                {
549
                    my $calendar = Koha::DiscreteCalendar->new( branchcode => $branchcode );
549
                    my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branchcode });
550
                    $days_between = $calendar->days_between( dt_from_string($data->{date_due}), $date_to_run );
550
                    $days_between = $calendar->days_between( dt_from_string($data->{date_due}), $date_to_run );
551
                }
551
                }
552
                else {
552
                else {
Lines 626-632 END_SQL Link Here
626
                my $exceededPrintNoticesMaxLines = 0;
626
                my $exceededPrintNoticesMaxLines = 0;
627
                while ( my $item_info = $sth2->fetchrow_hashref() ) {
627
                while ( my $item_info = $sth2->fetchrow_hashref() ) {
628
                    if ( C4::Context->preference('OverdueNoticeCalendar') ) {
628
                    if ( C4::Context->preference('OverdueNoticeCalendar') ) {
629
                        my $calendar = Koha::DiscreteCalendar->new( branchcode => $branchcode );
629
                        my $calendar = Koha::DiscreteCalendar->new({ branchcode => $branchcode });
630
                        $days_between = $calendar->days_between( dt_from_string( $item_info->{date_due} ), $date_to_run );
630
                        $days_between = $calendar->days_between( dt_from_string( $item_info->{date_due} ), $date_to_run );
631
                    }
631
                    }
632
                    else {
632
                    else {
(-)a/misc/cronjobs/staticfines.pl (-1 / +1 lines)
Lines 176-182 for ( my $i = 0 ; $i < scalar(@$data) ; $i++ ) { Link Here
176
176
177
    my $calendar;
177
    my $calendar;
178
    unless ( defined( $calendars{$branchcode} ) ) {
178
    unless ( defined( $calendars{$branchcode} ) ) {
179
        $calendars{$branchcode} = Koha::DiscreteCalendar->new( branchcode => $branchcode );
179
        $calendars{$branchcode} = Koha::DiscreteCalendar->new({ branchcode => $branchcode });
180
    }
180
    }
181
    $calendar = $calendars{$branchcode};
181
    $calendar = $calendars{$branchcode};
182
    my $isHoliday = $calendar->isHoliday( $tday, $tmonth, $tyear );
182
    my $isHoliday = $calendar->isHoliday( $tday, $tmonth, $tyear );
(-)a/misc/cronjobs/thirdparty/TalkingTech_itiva_outbound.pl (-1 / +1 lines)
Lines 294-300 sub GetWaitingHolds { Link Here
294
    $sth->execute();
294
    $sth->execute();
295
    my @results;
295
    my @results;
296
    while ( my $issue = $sth->fetchrow_hashref() ) {
296
    while ( my $issue = $sth->fetchrow_hashref() ) {
297
        my $calendar = Koha::DiscreteCalendar->new( branchcode => $issue->{'site'} );
297
        my $calendar = Koha::DiscreteCalendar->new({ branchcode => $issue->{'site'} });
298
298
299
        my $waiting_date = dt_from_string( $issue->{waitingdate}, 'sql' );
299
        my $waiting_date = dt_from_string( $issue->{waitingdate}, 'sql' );
300
        my $pickup_date = $waiting_date->clone->add( days => $pickupdelay );
300
        my $pickup_date = $waiting_date->clone->add( days => $pickupdelay );
(-)a/t/db_dependent/Circulation/CalcDateDue.t (-24 / +23 lines)
Lines 8-14 use DBI; Link Here
8
use DateTime;
8
use DateTime;
9
use t::lib::Mocks;
9
use t::lib::Mocks;
10
use t::lib::TestBuilder;
10
use t::lib::TestBuilder;
11
use C4::Calendar;
11
use Koha::DateUtils;
12
use Koha::DiscreteCalendar;
12
13
13
use_ok('C4::Circulation');
14
use_ok('C4::Circulation');
14
15
Lines 39-50 t::lib::Mocks::mock_preference('useDaysMode', 'Days'); Link Here
39
my $cache           = Koha::Caches->get_instance();
40
my $cache           = Koha::Caches->get_instance();
40
$cache->clear_from_cache('single_holidays');
41
$cache->clear_from_cache('single_holidays');
41
42
42
my $dateexpiry = '2013-01-01';
43
my $dateexpiry = dt_from_string->truncate(to => 'day')->add(days => 30, hours => 23, minutes => 59)->iso8601;
43
44
my $borrower = {categorycode => 'B', dateexpiry => $dateexpiry};
44
my $borrower = {categorycode => 'B', dateexpiry => $dateexpiry};
45
my $start_date = DateTime->new({year => 2013, month => 2, day => 9});
45
my $start_date =dt_from_string->truncate(to => 'day')->add(days => 60);
46
my $date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower );
46
my $date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower );
47
is($date, $dateexpiry . 'T23:59:00', 'date expiry');
47
is($date, $dateexpiry, 'date expiry');
48
$date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower, 1 );
48
$date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower, 1 );
49
49
50
50
Lines 53-83 t::lib::Mocks::mock_preference('ReturnBeforeExpiry', 1); Link Here
53
t::lib::Mocks::mock_preference('useDaysMode', 'noDays');
53
t::lib::Mocks::mock_preference('useDaysMode', 'noDays');
54
54
55
$borrower = {categorycode => 'B', dateexpiry => $dateexpiry};
55
$borrower = {categorycode => 'B', dateexpiry => $dateexpiry};
56
$start_date = DateTime->new({year => 2013, month => 2, day => 9});
56
$start_date =dt_from_string->truncate(to => 'day')->add(days => 60);
57
$date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower );
57
$date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower );
58
is($date, $dateexpiry . 'T23:59:00', 'date expiry with useDaysMode to noDays');
58
is($date, $dateexpiry, 'date expiry with useDaysMode to noDays');
59
59
60
# Let's add a special holiday on 2013-01-01. With ReturnBeforeExpiry and
60
# Let's add a special holiday on 2013-01-01. With ReturnBeforeExpiry and
61
# useDaysMode different from 'Days', return should forward the dateexpiry.
61
# useDaysMode different from 'Days', return should forward the dateexpiry.
62
my $calendar = C4::Calendar->new(branchcode => $branchcode);
62
my $calendar = Koha::DiscreteCalendar->new({branchcode => $branchcode});
63
$calendar->insert_single_holiday(
63
$calendar->edit_holiday({
64
    day             => 1,
64
    title => 'holidayTest',
65
    month           => 1,
65
    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
66
    year            => 2013,
66
    start_date => dt_from_string->add(days=>30),
67
    title           =>'holidayTest',
67
    end_date => dt_from_string->add(days=>30)
68
    description     => 'holidayDesc'
68
});
69
);
70
$date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower );
69
$date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower );
71
is($date, '2012-12-31T23:59:00', 'date expiry should be 2013-01-01 -1 day');
70
is($date, dt_from_string->truncate(to => 'day')->add(days=>29, hours=>23, minutes=>59), 'date expiry should be 2013-01-01 -1 day');
72
$calendar->insert_single_holiday(
71
73
    day             => 31,
72
$calendar->edit_holiday({
74
    month           => 12,
73
    title => 'holidayTest',
75
    year            => 2012,
74
    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
76
    title           =>'holidayTest',
75
    start_date => dt_from_string->add(days => 29),
77
    description     => 'holidayDesc'
76
    end_date => dt_from_string->add(days => 29)
78
);
77
});
79
$date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower );
78
$date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower );
80
is($date, '2012-12-30T23:59:00', 'date expiry should be 2013-01-01 -2 day');
79
is($date, dt_from_string->truncate(to => 'day')->add(days=> 28, hours=>23, minutes=>59), 'date expiry should be 2013-01-01 -2 day');
81
80
82
81
83
$date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower, 1 );
82
$date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower, 1 );
(-)a/t/db_dependent/DiscreteCalendar.t (-8 / +36 lines)
Lines 18-24 Link Here
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
use Test::More tests => 47;
21
use Test::More tests => 50;
22
use Test::MockModule;
22
use Test::MockModule;
23
23
24
use C4::Context;
24
use C4::Context;
Lines 53-60 isnt($branch1,'', "First branch to do tests. BranchCode => $branch1"); Link Here
53
isnt($branch2,'', "Second branch to do tests. BranchCode => $branch2");
53
isnt($branch2,'', "Second branch to do tests. BranchCode => $branch2");
54
54
55
#2 Calendars to make sure branches are treated separately.
55
#2 Calendars to make sure branches are treated separately.
56
my $calendar = Koha::DiscreteCalendar->new(branchcode => $branch1);
56
my $calendar = Koha::DiscreteCalendar->new({branchcode => $branch1});
57
my $calendar2 = Koha::DiscreteCalendar->new(branchcode => $branch2);
57
my $calendar2 = Koha::DiscreteCalendar->new({branchcode => $branch2});
58
58
59
my $unique_holiday = DateTime->today;
59
my $unique_holiday = DateTime->today;
60
$calendar->edit_holiday({
60
$calendar->edit_holiday({
Lines 68-73 is($calendar->is_opened($unique_holiday), 0, "Branch closed today : $unique_holi Link Here
68
my @unique_holidays = $calendar->get_unique_holidays();
68
my @unique_holidays = $calendar->get_unique_holidays();
69
is(scalar @unique_holidays, 1, "Set of exception holidays at 1");
69
is(scalar @unique_holidays, 1, "Set of exception holidays at 1");
70
70
71
my $yesterday = DateTime->today->subtract(days => 1);
72
$calendar->edit_holiday({
73
    title => "Single holiday Today",
74
    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
75
    start_date => $yesterday,
76
    end_date => $yesterday
77
});
78
is($calendar->is_opened($yesterday), 1, "Cannot edit dates in the past without override : $yesterday is not a holiday");
79
80
$calendar->edit_holiday({
81
    title => "Single holiday Today",
82
    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
83
    start_date => $yesterday,
84
    end_date => $yesterday,
85
    override => 1
86
});
87
is($calendar->is_opened($yesterday), 0, "Can edit dates in the past without override : $yesterday is a holiday");
88
89
71
my $days_between_start = DateTime->today;
90
my $days_between_start = DateTime->today;
72
my $days_between_end = DateTime->today->add(days => 6);
91
my $days_between_end = DateTime->today->add(days => 6);
73
my $days_between = $calendar->days_between($days_between_start, $days_between_end)->in_units('days');
92
my $days_between = $calendar->days_between($days_between_start, $days_between_end)->in_units('days');
Lines 108-114 $calendar->edit_holiday({ Link Here
108
    end_date=>$unique_holiday_range_end
127
    end_date=>$unique_holiday_range_end
109
});
128
});
110
@unique_holidays = $calendar->get_unique_holidays();
129
@unique_holidays = $calendar->get_unique_holidays();
111
is(scalar @unique_holidays, 7, "Set of exception holidays at 7");
130
is(scalar @unique_holidays, 8, "Set of exception holidays at 7");
112
131
113
my $repeatable_holiday_range_start = DateTime->today->add(days => 8);
132
my $repeatable_holiday_range_start = DateTime->today->add(days => 8);
114
my $repeatable_holiday_range_end = DateTime->today->add(days => 13);
133
my $repeatable_holiday_range_end = DateTime->today->add(days => 13);
Lines 126-137 is(scalar @repeatable_holidays, 7, "Set of repeatable holidays at 7"); Link Here
126
# item due      : 2017-01-24 11:00:00
145
# item due      : 2017-01-24 11:00:00
127
# item returned : 2017-01-26 10:00:00
146
# item returned : 2017-01-26 10:00:00
128
# Branch closed : 2017-01-25
147
# Branch closed : 2017-01-25
129
# Open/close hours : 8AM to 4PM (8h day)
148
# Open/close hours : 08:00 to 16:00 (8h day)
130
# Hours due : 5 hours on 2017-01-24 + 2 hours on 2017-01-26 = 7hours
149
# Hours due : 5 hours on 2017-01-24 + 2 hours on 2017-01-26 = 7hours
131
150
132
my $open_hours_since_start = DateTime->today->add(days => 40, hours => 11);
151
my $open_hours_since_start = dt_from_string->truncate(to => 'day')->add(days => 40, hours => 11);
133
my $open_hours_since_end = DateTime->today->add(days => 42, hours => 10);
152
my $open_hours_since_end = dt_from_string->truncate(to => 'day')->add(days => 42, hours => 10);
134
my $holiday_between =  DateTime->today->add(days => 41);
153
my $holiday_between =  dt_from_string->truncate(to => 'day')->add(days => 41);
135
$calendar->edit_holiday({
154
$calendar->edit_holiday({
136
    title => '',
155
    title => '',
137
    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{NONE},
156
    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{NONE},
Lines 199-204 $calendar->edit_holiday({ Link Here
199
});
218
});
200
is($calendar->is_opened($today), 1, "Today's holiday was removed");
219
is($calendar->is_opened($today), 1, "Today's holiday was removed");
201
220
221
$calendar->edit_holiday({
222
    title => '',
223
    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{NONE},
224
    start_date => $yesterday,
225
    end_date => $yesterday,
226
    override => 1
227
});
228
is($calendar->is_opened($yesterday), 1, "Yesterdays's holiday was removed with override");
229
202
my $new_open_hours = '08:00';
230
my $new_open_hours = '08:00';
203
$calendar->edit_holiday({
231
$calendar->edit_holiday({
204
    title => '',
232
    title => '',
(-)a/t/db_dependent/Hold.t (-6 / +14 lines)
Lines 22-28 use C4::Context; Link Here
22
use C4::Biblio qw( AddBiblio );
22
use C4::Biblio qw( AddBiblio );
23
use Koha::Database;
23
use Koha::Database;
24
use Koha::Libraries;
24
use Koha::Libraries;
25
use C4::Calendar;
25
use Koha::DiscreteCalendar;
26
use Koha::Patrons;
26
use Koha::Patrons;
27
use Koha::Holds;
27
use Koha::Holds;
28
use Koha::Item;
28
use Koha::Item;
Lines 67-73 my $hold = Koha::Hold->new( Link Here
67
    {
67
    {
68
        biblionumber   => $biblionumber,
68
        biblionumber   => $biblionumber,
69
        itemnumber     => $item->id(),
69
        itemnumber     => $item->id(),
70
        reservedate    => '2017-01-01',
70
        reservedate    => dt_from_string->subtract(days => 2),
71
        waitingdate    => '2000-01-01',
71
        waitingdate    => '2000-01-01',
72
        borrowernumber => $borrower->{borrowernumber},
72
        borrowernumber => $borrower->{borrowernumber},
73
        branchcode     => $branches[1]->{branchcode},
73
        branchcode     => $branches[1]->{branchcode},
Lines 76-86 my $hold = Koha::Hold->new( Link Here
76
);
76
);
77
$hold->store();
77
$hold->store();
78
78
79
my $b1_cal = C4::Calendar->new( branchcode => $branches[1]->{branchcode} );
79
my $b1_cal = Koha::DiscreteCalendar->new({ branchcode => $branches[1]->{branchcode} });
80
$b1_cal->insert_single_holiday( day => 02, month => 01, year => 2017, title => "Morty Day", description => "Rick" ); #Add a holiday
80
my $holiday = dt_from_string->subtract(days => 1);
81
$b1_cal->edit_holiday({
82
    title => "Morty Day",
83
    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
84
    start_date => $holiday,
85
    end_date => $holiday,
86
    override => 1
87
}); #Add a holiday
88
81
my $today = dt_from_string;
89
my $today = dt_from_string;
82
is( $hold->age(), $today->delta_days( dt_from_string( '2017-01-01' ) )->in_units( 'days')  , "Age of hold is days from reservedate to now if calendar ignored");
90
is( $hold->age(), $today->delta_days( dt_from_string->subtract(days => 2) )->in_units( 'days' ), "Age of hold is days from reservedate to now if calendar ignored");
83
is( $hold->age(1), $today->delta_days( dt_from_string( '2017-01-01' ) )->in_units( 'days' ) - 1 , "Age of hold is days from reservedate to now minus 1 if calendar used");
91
is( $hold->age(1), $today->delta_days( dt_from_string->subtract(days => 2) )->in_units( 'days' ) - 1, "Age of hold is days from reservedate to now minus 1 if calendar used");
84
92
85
is( $hold->suspend, 0, "Hold is not suspended" );
93
is( $hold->suspend, 0, "Hold is not suspended" );
86
$hold->suspend_hold();
94
$hold->suspend_hold();
(-)a/t/db_dependent/Holds.t (-1 lines)
Lines 13-19 use Koha::Patrons; Link Here
13
use C4::Items;
13
use C4::Items;
14
use C4::Biblio;
14
use C4::Biblio;
15
use C4::Reserves;
15
use C4::Reserves;
16
use C4::Calendar;
17
16
18
use Koha::Database;
17
use Koha::Database;
19
use Koha::DateUtils qw( dt_from_string output_pref );
18
use Koha::DateUtils qw( dt_from_string output_pref );
(-)a/t/db_dependent/HoldsQueue.t (-6 / +6 lines)
Lines 200-208 $schema->resultset('DiscreteCalendar')->search({ Link Here
200
    close_hour   => '16:00:00'
200
    close_hour   => '16:00:00'
201
});
201
});
202
202
203
Koha::DiscreteCalendar->new( branchcode => '' )->add_new_branch('', $library1->{branchcode});
203
Koha::DiscreteCalendar->new({ branchcode => '' })->add_new_branch('', $library1->{branchcode});
204
Koha::DiscreteCalendar->new( branchcode => '' )->add_new_branch('', $library2->{branchcode});
204
Koha::DiscreteCalendar->new({ branchcode => '' })->add_new_branch('', $library2->{branchcode});
205
Koha::DiscreteCalendar->new( branchcode => '' )->add_new_branch('', $library3->{branchcode});
205
Koha::DiscreteCalendar->new({ branchcode => '' })->add_new_branch('', $library3->{branchcode});
206
206
207
@branchcodes = ( $library1->{branchcode}, $library2->{branchcode}, $library3->{branchcode} );
207
@branchcodes = ( $library1->{branchcode}, $library2->{branchcode}, $library3->{branchcode} );
208
208
Lines 320-333 my $today = dt_from_string(); Link Here
320
320
321
# If the test below is removed, aother tests using the holiday will fail. For some reason if we call is_holiday now
321
# If the test below is removed, aother tests using the holiday will fail. For some reason if we call is_holiday now
322
# the holiday will get set in cache correctly, but not if we let C4::HoldsQueue call is_holiday instead.
322
# the holiday will get set in cache correctly, but not if we let C4::HoldsQueue call is_holiday instead.
323
Koha::DiscreteCalendar->new( branchcode => $branchcodes[0] )->edit_holiday({
323
Koha::DiscreteCalendar->new({ branchcode => $branchcodes[0] })->edit_holiday({
324
    title        => "Today",
324
    title        => "Today",
325
    holiday_type => "E",
325
    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
326
    start_date   => $today,
326
    start_date   => $today,
327
    end_date     => $today
327
    end_date     => $today
328
});
328
});
329
329
330
is( Koha::DiscreteCalendar->new( branchcode => $branchcodes[0] )->is_holiday( $today ), 1, 'Is today a holiday for pickup branch' );
330
is( Koha::DiscreteCalendar->new({ branchcode => $branchcodes[0] })->is_holiday( $today ), 1, 'Is today a holiday for pickup branch' );
331
C4::HoldsQueue::CreateQueue();
331
C4::HoldsQueue::CreateQueue();
332
$holds_queue = $dbh->selectall_arrayref("SELECT * FROM tmp_holdsqueue", { Slice => {} });
332
$holds_queue = $dbh->selectall_arrayref("SELECT * FROM tmp_holdsqueue", { Slice => {} });
333
is( scalar( @$holds_queue ), 1, "Holds not filled with items from closed libraries" );
333
is( scalar( @$holds_queue ), 1, "Holds not filled with items from closed libraries" );
(-)a/tools/discrete_calendar.pl (-4 / +4 lines)
Lines 16-23 Link Here
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
17
18
#####Sets holiday periods for each branch. Datedues will be extended if branch is closed -TG
18
#####Sets holiday periods for each branch. Datedues will be extended if branch is closed -TG
19
use strict;
19
use Modern::Perl;
20
use warnings;
21
20
22
use CGI qw ( -utf8 );
21
use CGI qw ( -utf8 );
23
22
Lines 40-46 my ($template, $loggedinuser, $cookie) Link Here
40
                           });
39
                           });
41
40
42
my $branch = $input->param('branch') || C4::Context->userenv->{'branch'};
41
my $branch = $input->param('branch') || C4::Context->userenv->{'branch'};
43
my $calendar = Koha::DiscreteCalendar->new(branchcode => $branch);
42
my $calendar = Koha::DiscreteCalendar->new({branchcode => $branch});
44
#alert the user that they are using the default calendar because they do not have a library set
43
#alert the user that they are using the default calendar because they do not have a library set
45
my $no_branch_selected = $calendar->{no_branch_selected};
44
my $no_branch_selected = $calendar->{no_branch_selected};
46
45
Lines 89-94 if($action eq 'copyBranch'){ Link Here
89
        $endDate = $startDate->clone();
88
        $endDate = $startDate->clone();
90
    }
89
    }
91
90
91
    warn $startDate;
92
    warn $endDate;
92
    $calendar->edit_holiday( {
93
    $calendar->edit_holiday( {
93
        title        => $title,
94
        title        => $title,
94
        weekday      => $weekday,
95
        weekday      => $weekday,
95
- 

Return to bug 17015