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

(-)a/Koha/DiscreteCalendar.pm (+1298 lines)
Line 0 Link Here
1
package Koha::DiscreteCalendar;
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
#####Sets holiday periods for each branch. Datedues will be extended if branch is closed -TG
19
use strict;
20
use warnings;
21
22
use CGI qw ( -utf8 );
23
use Carp;
24
use DateTime;
25
use DateTime::Format::Strptime;
26
27
use C4::Context;
28
use C4::Output;
29
use Koha::Database;
30
use Koha::DateUtils;
31
32
# Global variables to make code more readable
33
our $HOLIDAYS = {
34
    EXCEPTION => 'E',
35
    REPEATABLE => 'R',
36
    SINGLE => 'S',
37
    NEED_VALIDATION => 'N',
38
    FLOAT => 'F',
39
    WEEKLY => 'W',
40
    NONE => 'none'
41
};
42
43
=head1 NAME
44
45
Koha::DiscreteCalendar - Object containing a branches calendar, working with the SQL database
46
47
=head1 SYNOPSIS
48
49
  use Koha::DiscreteCalendar
50
51
  my $c = Koha::DiscreteCalendar->new( branchcode => 'MAIN' );
52
  my $dt = DateTime->now();
53
54
  # are we open
55
  $open = $c->is_holiday($dt);
56
  # when will item be due if loan period = $dur (a DateTime::Duration object)
57
  $duedate = $c->addDate($dt,$dur,'days');
58
59
60
=head1 DESCRIPTION
61
62
  Implements a new Calendar object, but uses the SQL database to keep track of days and holidays.
63
  This results in a performance gain since the optimization is done by the MySQL database/team.
64
65
=head1 METHODS
66
67
=head2 new : Create a (discrete) calendar object
68
69
my $calendar = Koha::DiscreteCalendar->new( branchcode => 'MAIN' );
70
71
The option branchcode is required
72
73
=cut
74
75
sub new {
76
    my ( $classname, %options ) = @_;
77
    my $self = {};
78
    bless $self, $classname;
79
    for my $o_name ( keys %options ) {
80
        my $o = lc $o_name;
81
        $self->{$o} = $options{$o_name};
82
    }
83
    if ( !defined $self->{branchcode} ) {
84
        croak 'No branchcode argument passed to Koha::DiscreteCalendar->new';
85
    }
86
    $self->_init();
87
88
    return $self;
89
}
90
91
sub _init {
92
    my $self = shift;
93
    $self->{days_mode} = C4::Context->preference('useDaysMode');
94
    #If the branchcode doesn't exist we use the default calendar.
95
    my $schema = Koha::Database->new->schema;
96
    my $branchcode = $self->{branchcode};
97
    my $dtf = $schema->storage->datetime_parser;
98
    my $today = $dtf->format_datetime(DateTime->today);
99
    my $rs = $schema->resultset('DiscreteCalendar')->single(
100
        {
101
            branchcode  => $branchcode,
102
            date        => $today
103
        }
104
    );
105
    #use default if no calendar is found
106
    if (!$rs){
107
        $self->{branchcode} = '';
108
        $self->{no_branch_selected} = 1;
109
    }
110
111
}
112
113
=head2 get_dates_info
114
115
  my @dates = $calendar->get_dates_info();
116
117
Returns an array of hashes representing the dates in this calendar. The hash
118
contains the fields C<$date>, C<$outputdate>, C<$holiday_type>, C<$open_hour>,
119
C<$close_hour> and C<$note>.
120
121
=cut
122
123
sub get_dates_info {
124
    my $self = shift;
125
    my $branchcode = $self->{branchcode};
126
    my @datesInfos =();
127
    my $schema = Koha::Database->new->schema;
128
129
    my $rs = $schema->resultset('DiscreteCalendar')->search(
130
        {
131
            branchcode  => $branchcode
132
        },
133
        {
134
            select  => [ 'date', { DATE => 'date' } ],
135
            as      => [qw/ date date /],
136
            columns =>[ qw/ holiday_type open_hour close_hour note/]
137
        },
138
    );
139
140
    while (my $date = $rs->next()){
141
        my $outputdate = dt_from_string( $date->date(), 'iso');
142
        $outputdate = output_pref( { dt => $outputdate, dateonly => 1 } );
143
        push @datesInfos, {
144
            date        => $date->date(),
145
            outputdate  => $outputdate,
146
            holiday_type => $date->holiday_type() ,
147
            open_hour    => $date->open_hour(),
148
            close_hour   => $date->close_hour(),
149
            note        => $date->note()
150
        };
151
    }
152
153
    return @datesInfos;
154
}
155
156
=head2 add_new_branch
157
158
    Koha::DiscreteCalendar->add_new_branch($copyBranch, $newBranch)
159
160
This methode will copy everything from a given branch to a new branch
161
C<$copyBranch> is the branch to copy from
162
C<$newBranch> is the branch to be created, and copy into
163
164
=cut
165
166
sub add_new_branch {
167
    my ( $classname, $copyBranch, $newBranch) = @_;
168
169
    $copyBranch = '' unless $copyBranch;
170
    my $schema = Koha::Database->new->schema;
171
172
    my $branch_rs = $schema->resultset('DiscreteCalendar')->search({
173
            branchcode => $copyBranch
174
    });
175
176
    while(my $row = $branch_rs->next()){
177
        $schema->resultset('DiscreteCalendar')->create({
178
            date        => $row->date(),
179
            branchcode  => $newBranch,
180
            is_opened    => $row->is_opened(),
181
            holiday_type => $row->holiday_type(),
182
            open_hour    => $row->open_hour(),
183
            close_hour   => $row->close_hour(),
184
        });
185
    }
186
187
}
188
189
=head2 get_date_info
190
191
    my $date = $calendar->get_date_info;
192
193
Returns a reference-to-hash representing a DiscreteCalendar date data object.
194
The hash contains the fields C<$date>, C<$outputdate>, C<$holiday_type>,
195
C<$open_hour>, C<$close_hour> and C<$note>.
196
197
=cut
198
199
sub get_date_info {
200
    my ($self, $date) = @_;
201
    my $branchcode = $self->{branchcode};
202
    my $schema = Koha::Database->new->schema;
203
    my $dtf = $schema->storage->datetime_parser;
204
    #String dates for Database usage
205
    my $date_string = $dtf->format_datetime($date);
206
207
    my $rs = $schema->resultset('DiscreteCalendar')->search(
208
        {
209
            branchcode  => $branchcode,
210
        },
211
        {
212
            select  => [ 'date', { DATE => 'date' } ],
213
            as      => [qw/ date date /],
214
            where   => \['DATE(?) = date', $date_string ],
215
            columns =>[ qw/ branchcode holiday_type open_hour close_hour note/]
216
        },
217
    );
218
    my $dateDTO;
219
    while (my $date = $rs->next()){
220
        $dateDTO = {
221
            date        => $date->date(),
222
            branchcode  => $date->branchcode(),
223
            holiday_type => $date->holiday_type() ,
224
            open_hour    => $date->open_hour(),
225
            close_hour   => $date->close_hour(),
226
            note        => $date->note()
227
        };
228
    }
229
230
    return $dateDTO;
231
}
232
233
=head2 get_max_date
234
235
    my $maxDate = $calendar->get_max_date();
236
237
Returns the furthest date available in the databse of current branch.
238
239
=cut
240
241
sub get_max_date {
242
    my $self       = shift;
243
    my $branchcode     = $self->{branchcode};
244
    my $schema = Koha::Database->new->schema;
245
246
    my $rs = $schema->resultset('DiscreteCalendar')->search(
247
        {
248
            branchcode  => $branchcode
249
        },
250
        {
251
            select => [{ MAX => 'date' } ],
252
            as     => [qw/ max /],
253
        }
254
    );
255
256
    return $rs->next()->get_column('max');
257
}
258
259
=head2 get_min_date
260
261
    my $minDate = $calendar->get_min_date();
262
263
Returns the oldest date available in the databse of current branch.
264
265
=cut
266
267
sub get_min_date {
268
    my $self       = shift;
269
    my $branchcode     = $self->{branchcode};
270
    my $schema = Koha::Database->new->schema;
271
272
    my $rs = $schema->resultset('DiscreteCalendar')->search(
273
        {
274
            branchcode  => $branchcode
275
        },
276
        {
277
            select => [{ MIN => 'date' } ],
278
            as     => [qw/ min /],
279
        }
280
    );
281
282
    return $rs->next()->get_column('min');
283
}
284
285
=head2 get_unique_holidays
286
287
  my @unique_holidays = $calendar->get_unique_holidays();
288
289
Returns an array of all the date objects that are unique holidays.
290
291
=cut
292
293
sub get_unique_holidays {
294
    my $self = shift;
295
    my $branchcode = $self->{branchcode};
296
    my @unique_holidays;
297
    my $schema = Koha::Database->new->schema;
298
299
    my $rs = $schema->resultset('DiscreteCalendar')->search(
300
        {
301
            branchcode  => $branchcode,
302
            holiday_type => $HOLIDAYS->{EXCEPTION}
303
        },
304
        {
305
            select => [{ DATE => 'date' }, 'note' ],
306
            as     => [qw/ date note/],
307
        }
308
    );
309
    while (my $date = $rs->next()){
310
        my $outputdate = dt_from_string($date->date(), 'iso');
311
        $outputdate = output_pref( { dt => $outputdate, dateonly => 1 } );
312
        push @unique_holidays, {
313
            date =>  $date->date(),
314
            outputdate => $outputdate,
315
            note => $date->note()
316
        }
317
    }
318
319
    return @unique_holidays;
320
}
321
322
=head2 get_float_holidays
323
324
  my @float_holidays = $calendar->get_float_holidays();
325
326
Returns an array of all the date objects that are float holidays.
327
328
=cut
329
330
sub get_float_holidays {
331
    my $self = shift;
332
    my $branchcode = $self->{branchcode};
333
    my @float_holidays;
334
    my $schema = Koha::Database->new->schema;
335
336
    my $rs = $schema->resultset('DiscreteCalendar')->search(
337
        {
338
            branchcode  => $branchcode,
339
            holiday_type => $HOLIDAYS->{FLOAT}
340
        },
341
        {
342
            select => [{ DATE => 'date' }, 'note' ],
343
            as     => [qw/ date note/],
344
        }
345
    );
346
    while (my $date = $rs->next()){
347
        my $outputdate = dt_from_string($date->date(), 'iso');
348
        $outputdate = output_pref( { dt => $outputdate, dateonly => 1 } );
349
        push @float_holidays, {
350
            date        =>  $date->date(),
351
            outputdate  => $outputdate,
352
            note        => $date->note()
353
        }
354
    }
355
356
    return @float_holidays;
357
}
358
359
=head2 get_need_validation_holidays
360
361
  my @need_validation_holidays = $calendar->get_need_validation_holidays();
362
363
Returns an array of all the date objects that are float holidays in need of validation.
364
365
=cut
366
367
sub get_need_validation_holidays {
368
    my $self = shift;
369
    my $branchcode = $self->{branchcode};
370
    my @need_validation_holidays;
371
    my $schema = Koha::Database->new->schema;
372
373
    my $rs = $schema->resultset('DiscreteCalendar')->search(
374
        {
375
            branchcode  => $branchcode,
376
            holiday_type => $HOLIDAYS->{NEED_VALIDATION}
377
        },
378
        {
379
            select => [{ DATE => 'date' }, 'note' ],
380
            as     => [qw/ date note/],
381
        }
382
    );
383
    while (my $date = $rs->next()){
384
        my $outputdate = dt_from_string($date->date(), 'iso');
385
        $outputdate = output_pref( { dt => $outputdate, dateonly => 1 } );
386
        push @need_validation_holidays, {
387
            date        =>  $date->date(),
388
            outputdate  => $outputdate,
389
            note        => $date->note()
390
        }
391
    }
392
393
    return @need_validation_holidays;
394
}
395
396
=head2 get_repeatable_holidays
397
398
  my @repeatable_holidays = $calendar->get_repeatable_holidays();
399
400
Returns an array of all the date objects that are repeatable holidays.
401
402
=cut
403
404
sub get_repeatable_holidays {
405
    my $self = shift;
406
    my $branchcode = $self->{branchcode};
407
    my @repeatable_holidays;
408
    my $schema = Koha::Database->new->schema;
409
410
    my $rs = $schema->resultset('DiscreteCalendar')->search(
411
        {
412
            branchcode  => $branchcode,
413
            holiday_type => $HOLIDAYS->{'REPEATABLE'},
414
415
        },
416
        {
417
            select  => \[ 'distinct DAY(date), MONTH(date), note'],
418
            as      => [qw/ day month note/],
419
        }
420
    );
421
422
    while (my $date = $rs->next()){
423
        push @repeatable_holidays, {
424
            day=> $date->get_column('day'),
425
            month => $date->get_column('month'),
426
            note => $date->note()
427
        };
428
    }
429
430
    return @repeatable_holidays;
431
}
432
433
=head2 get_week_days_holidays
434
435
  my @week_days_holidays = $calendar->get_week_days_holidays;
436
437
Returns an array of all the date objects that are weekly holidays.
438
439
=cut
440
441
sub get_week_days_holidays {
442
    my $self = shift;
443
    my $branchcode = $self->{branchcode};
444
    my @week_days;
445
    my $schema = Koha::Database->new->schema;
446
447
    my $rs = $schema->resultset('DiscreteCalendar')->search(
448
        {
449
            holiday_type => $HOLIDAYS->{WEEKLY},
450
            branchcode  => $branchcode,
451
        },
452
        {
453
            select      => [{ DAYOFWEEK => 'date'}, 'note'],
454
            as          => [qw/ weekday note /],
455
            distinct    => 1,
456
        }
457
    );
458
459
    while (my $date = $rs->next()){
460
        push @week_days, {
461
            weekday => ($date->get_column('weekday') -1),
462
            note    => $date->note()
463
        };
464
    }
465
466
    return @week_days;
467
}
468
469
=head2 edit_holiday
470
471
Modifies a date or a range of dates
472
473
C<$title> Is the title to be modified for the holiday formed by $year/$month/$day.
474
475
C<$weekday> Is the day of week for the holiday or the value everyday when it's for the whole range.
476
477
C<$holiday_type> Is the type of the holiday :
478
    E : Exception holiday, single day.
479
    F : Floating holiday, different day each year.
480
    N : Needs validation, copied float holiday from the past
481
    R : Repeatable holiday, repeated on same date.
482
    W : Weekly holiday, same day of the week.
483
484
C<$open_hour> Is the opening hour.
485
C<$close_hour> Is the closing hour.
486
C<$start_date> Is the start of the range of dates.
487
C<$end_date> Is the end of the range of dates.
488
C<$delete_type> Delete all
489
C<$today> Today based on the local date, using JavaScript.
490
491
=cut
492
493
sub edit_holiday {
494
    my $self = shift;
495
    my ($params) = @_;
496
497
    my $title        = $params->{title};
498
    my $weekday      = $params->{weekday} || '';
499
    my $holiday_type = $params->{holiday_type};
500
501
    my $start_date   = $params->{start_date};
502
    my $end_date     = $params->{end_date};
503
504
    my $open_hour    = $params->{open_hour} || '';
505
    my $close_hour   = $params->{close_hour} || '';
506
507
    my $delete_type  = $params->{delete_type} || undef;
508
    my $today        = $params->{today} || DateTime->today;
509
510
    my $branchcode = $self->{branchcode};
511
512
    my $schema = Koha::Database->new->schema;
513
    $schema->{AutoCommit} = 0;
514
    $schema->storage->txn_begin;
515
    my $dtf = $schema->storage->datetime_parser;
516
517
    #String dates for Database usage
518
    my $start_date_string = $dtf->format_datetime($start_date);
519
    my $end_date_string = $dtf->format_datetime($end_date);
520
    $today = $dtf->format_datetime($today);
521
522
    my %updateValues = (
523
        is_opened    => 0,
524
        note        => $title,
525
        holiday_type => $holiday_type,
526
    );
527
    $updateValues{open_hour}  = $open_hour if $open_hour ne '';
528
    $updateValues{close_hour}  = $close_hour if $close_hour ne '';
529
530
    if($holiday_type eq $HOLIDAYS->{WEEKLY}) {
531
        #Update weekly holidays
532
        if($start_date_string eq $end_date_string ){
533
            $end_date_string = $self->get_max_date();
534
        }
535
        my $rs = $schema->resultset('DiscreteCalendar')->search(
536
            {
537
                branchcode  => $branchcode,
538
            },
539
            {
540
                where => \[ 'DAYOFWEEK(date) = ? AND date >= ? AND date <= DATE(?)', $weekday, $start_date_string, $end_date_string],
541
            }
542
        );
543
544
        while (my $date = $rs->next()){
545
            $date->update(\%updateValues);
546
        }
547
    }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
549
        my $where = { date => { -between => [$start_date_string, $end_date_string], '>=' => $today}};
550
        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}]};
552
        }
553
        my $rs = $schema->resultset('DiscreteCalendar')->search(
554
            {
555
                branchcode  => $branchcode,
556
            },
557
            {
558
                where =>  $where,
559
            }
560
        );
561
        while (my $date = $rs->next()){
562
            $date->update(\%updateValues);
563
        }
564
565
    }elsif ($holiday_type eq $HOLIDAYS->{REPEATABLE}) {
566
        #Update repeatable holidays
567
        my $parser = DateTime::Format::Strptime->new(
568
           pattern  => '%m-%d',
569
           on_error => 'croak',
570
        );
571
        #Format the dates to have only month-day ex: 01-04 for January 4th
572
        $start_date = $parser->format_datetime($start_date);
573
        $end_date = $parser->format_datetime($end_date);
574
        my $rs = $schema->resultset('DiscreteCalendar')->search(
575
            {
576
                branchcode  => $branchcode,
577
            },
578
            {
579
                where =>  { -and => [date => { '>=' => $today}, \["(DATE_FORMAT(date,'\%m-\%d') BETWEEN ? AND ?)", $start_date, $end_date]]},
580
            }
581
        );
582
        while (my $date = $rs->next()){
583
            $date->update(\%updateValues);
584
        }
585
586
    }else {
587
        #Update date(s)/Remove holidays
588
        my $where = { date => { -between => [$start_date_string, $end_date_string], '>=' => $today}};
589
        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}]};
591
        }
592
        my $rs = $schema->resultset('DiscreteCalendar')->search(
593
            {
594
                branchcode  => $branchcode,
595
            },
596
            {
597
                where =>  $where,
598
            }
599
        );
600
        #If none, the date(s) will be normal days, else,
601
        if($holiday_type eq 'none'){
602
            $updateValues{holiday_type}  ='';
603
            $updateValues{is_opened}  =1;
604
        }else{
605
            delete $updateValues{holiday_type};
606
        }
607
608
        while (my $date = $rs->next()){
609
            if($delete_type){
610
                if($date->holiday_type() eq $HOLIDAYS->{WEEKLY}){
611
                    $self->remove_weekly_holidays($weekday, \%updateValues, $today);
612
                }elsif($date->holiday_type() eq $HOLIDAYS->{REPEATABLE}){
613
                    $self->remove_repeatable_holidays($start_date, $end_date, \%updateValues, $today);
614
                }
615
            }else{
616
                $date->update(\%updateValues);
617
            }
618
        }
619
    }
620
    $schema->storage->txn_commit;
621
}
622
623
=head2 remove_weekly_holidays
624
625
    $calendar->remove_weekly_holidays($weekday, $updateValues, $today);
626
627
Removes a weekly holiday and updates the days' parameters
628
C<$weekday> is the weekday to un-holiday
629
C<$updatevalues> is hashref containing the new parameters
630
C<$today> is today's date
631
632
=cut
633
634
sub remove_weekly_holidays {
635
    my ($self, $weekday, $updateValues, $today) = @_;
636
    my $branchcode = $self->{branchcode};
637
    my $schema = Koha::Database->new->schema;
638
639
    my $rs = $schema->resultset('DiscreteCalendar')->search(
640
        {
641
            branchcode  => $branchcode,
642
            is_opened    => 0,
643
            holiday_type => $HOLIDAYS->{WEEKLY}
644
        },
645
        {
646
            where => {-and => [ \["DAYOFWEEK(date) = ?", $weekday], date => { '>=' => $today}]},
647
        }
648
    );
649
650
    while (my $date = $rs->next()){
651
        $date->update($updateValues);
652
    }
653
}
654
655
=head2 remove_repeatable_holidays
656
657
    $calendar->remove_repeatable_holidays($startDate, $endDate, $today);
658
659
Removes a repeatable holiday and updates the days' parameters
660
C<$startDatey> is the start date of the repeatable holiday
661
C<$endDate> is the end date of the repeatble holiday
662
C<$updatevalues> is hashref containing the new parameters
663
C<$today> is today's date
664
665
=cut
666
667
sub remove_repeatable_holidays {
668
    my ($self, $startDate, $endDate, $updateValues, $today) = @_;
669
    my $branchcode = $self->{branchcode};
670
    my $schema = Koha::Database->new->schema;
671
    my $parser = DateTime::Format::Strptime->new(
672
        pattern   => '%m-%d',
673
        on_error  => 'croak',
674
    );
675
    #Format the dates to have only month-day ex: 01-04 for January 4th
676
    $startDate = $parser->format_datetime($startDate);
677
    $endDate = $parser->format_datetime($endDate);
678
679
    my $rs = $schema->resultset('DiscreteCalendar')->search(
680
        {
681
            branchcode  => $branchcode,
682
            is_opened    => 0,
683
            holiday_type => $HOLIDAYS->{REPEATABLE},
684
        },
685
        {
686
            where =>  { -and => [date => { '>=' => $today}, \["(DATE_FORMAT(date,'\%m-\%d') BETWEEN ? AND ?)", $startDate, $endDate]]},
687
        }
688
    );
689
690
    while (my $date = $rs->next()){
691
        $date->update($updateValues);
692
    }
693
}
694
695
=head2 copy_to_branch
696
697
  $calendar->copy_to_branch($branch2);
698
699
Copies the days and holidays from this branch to $branch2, ignoring dates in C<$self>
700
but not in C<$branch2>
701
702
C<$branch2> the branch to copy into
703
704
=cut
705
706
sub copy_to_branch {
707
    my ($self,$newBranch) =@_;
708
    my $branchcode = $self->{branchcode};
709
    my $schema = Koha::Database->new->schema;
710
711
    my $copyFrom = $schema->resultset('DiscreteCalendar')->search(
712
        {
713
            branchcode  => $branchcode
714
        },
715
        {
716
            columns     => [ qw/ date is_opened note holiday_type open_hour close_hour /]
717
        }
718
    );
719
    while (my $copyDate = $copyFrom->next()){
720
        my $copyTo = $schema->resultset('DiscreteCalendar')->search(
721
            {
722
                branchcode  => $newBranch,
723
                date        => $copyDate->date(),
724
            },
725
            {
726
                columns => [ qw/ date branchcode is_opened note holiday_type open_hour close_hour /]
727
            }
728
        );
729
        #if the date does not exist in the copyTO branch, than skip it.
730
        if($copyTo->count ==0){
731
            next;
732
        }
733
        $copyTo->next()->update({
734
            is_opened    => $copyDate->is_opened(),
735
            holiday_type => $copyDate->holiday_type(),
736
            note        => $copyDate->note(),
737
            open_hour    => $copyDate->open_hour(),
738
            close_hour   => $copyDate->close_hour()
739
        });
740
    }
741
}
742
743
=head2 is_opened
744
745
    $calendar->is_opened($date)
746
747
Returns whether the library is open on C<$date>
748
749
=cut
750
751
sub is_opened {
752
    my($self, $date) = @_;
753
    my $branchcode = $self->{branchcode};
754
    my $schema = Koha::Database->new->schema;
755
    my $dtf = $schema->storage->datetime_parser;
756
    $date= $dtf->format_datetime($date);
757
    #if the date is not found
758
    my $is_opened = -1;
759
    my $rs = $schema->resultset('DiscreteCalendar')->search(
760
        {
761
            branchcode => $branchcode,
762
        },
763
        {
764
            where   => \['date = DATE(?)', $date]
765
        }
766
    );
767
    $is_opened = $rs->next()->is_opened() if $rs->count() != 0;
768
769
    return $is_opened;
770
}
771
772
=head2 is_holiday
773
774
    $calendar->is_holiday($date)
775
776
Returns whether C<$date> is a holiday or not
777
778
=cut
779
780
sub is_holiday {
781
    my($self, $date) = @_;
782
    my $branchcode = $self->{branchcode};
783
    my $schema = Koha::Database->new->schema;
784
    my $dtf = $schema->storage->datetime_parser;
785
    $date= $dtf->format_datetime($date);
786
    #if the date is not found
787
    my $isHoliday = -1;
788
    my $rs = $schema->resultset('DiscreteCalendar')->search(
789
        {
790
            branchcode => $branchcode,
791
        },
792
        {
793
            where   => \['date = DATE(?)', $date]
794
        }
795
    );
796
797
    if($rs->count() != 0){
798
        $isHoliday = 0 if $rs->first()->is_opened();
799
        $isHoliday = 1 if !$rs->first()->is_opened();
800
    }
801
802
    return $isHoliday;
803
}
804
805
=head2 copy_holiday
806
807
  $calendar->copy_holiday($from_startDate, $from_endDate, $to_startDate, $to_endDate, $daysnumber);
808
809
Copies a holiday's parameters from a range to a new range
810
C<$from_startDate> the source holiday's start date
811
C<$from_endDate> the source holiday's end date
812
C<$to_startDate> the destination holiday's start date
813
C<$to_endDate> the destination holiday's end date
814
C<$daysnumber> the number of days in the range.
815
816
Both ranges should have the same number of days in them.
817
818
=cut
819
820
sub copy_holiday {
821
    my ($self, $from_startDate, $from_endDate, $to_startDate, $to_endDate, $daysnumber) = @_;
822
    my $branchcode = $self->{branchcode};
823
    my $copyFromType =  $from_startDate && $from_endDate eq '' ? 'oneDay': 'range';
824
    my $schema = Koha::Database->new->schema;
825
    my $dtf = $schema->storage->datetime_parser;
826
827
    if ($copyFromType eq 'oneDay'){
828
        my $where;
829
        $to_startDate = $dtf->format_datetime($to_startDate);
830
        if ($to_startDate && $to_endDate) {
831
            $to_endDate = $dtf->format_datetime($to_endDate);
832
            $where = { date => { -between => [$to_startDate, $to_endDate]}};
833
        } else {
834
            $where = { date => $to_startDate };
835
        }
836
837
        $from_startDate = $dtf->format_datetime($from_startDate);
838
        my $fromDate = $schema->resultset('DiscreteCalendar')->search(
839
            {
840
                branchcode  => $branchcode,
841
                date        => $from_startDate
842
            }
843
        );
844
        my $toDates = $schema->resultset('DiscreteCalendar')->search(
845
            {
846
                branchcode  => $branchcode,
847
            },
848
            {
849
                where       => $where
850
            }
851
        );
852
853
        my $copyDate = $fromDate->next();
854
        while (my $date = $toDates->next()){
855
            $date->update({
856
                is_opened    => $copyDate->is_opened(),
857
                holiday_type => $copyDate->holiday_type(),
858
                note        => $copyDate->note(),
859
                open_hour    => $copyDate->open_hour(),
860
                close_hour   => $copyDate->close_hour()
861
            })
862
        }
863
864
    }else{
865
        my $endDate = dt_from_string($from_endDate);
866
        $to_startDate = $dtf->format_datetime($to_startDate);
867
        $to_endDate = $dtf->format_datetime($to_endDate);
868
        if($daysnumber == 7){
869
            for (my $tempDate = $from_startDate->clone(); $tempDate <= $endDate;$tempDate->add(days => 1)){
870
                my $formatedDate = $dtf->format_datetime($tempDate);
871
                my $fromDate = $schema->resultset('DiscreteCalendar')->search(
872
                    {
873
                        branchcode  => $branchcode,
874
                        date        => $formatedDate,
875
                    },
876
                    {
877
                        select  => [{ DAYOFWEEK => 'date' }],
878
                        as      => [qw/ weekday /],
879
                        columns =>[ qw/ holiday_type note open_hour close_hour note/]
880
                    }
881
                );
882
                my $copyDate = $fromDate->next();
883
                my $weekday = $copyDate->get_column('weekday');
884
885
                my $toDate = $schema->resultset('DiscreteCalendar')->search(
886
                    {
887
                        branchcode  => $branchcode,
888
889
                    },
890
                    {
891
                        where       => {date => {-between => [$to_startDate, $to_endDate]}, "DAYOFWEEK(date)" => $weekday},
892
                    }
893
                );
894
                my $copyToDate = $toDate->next();
895
                $copyToDate->update({
896
                    is_opened    => $copyDate->is_opened(),
897
                    holiday_type => $copyDate->holiday_type(),
898
                    note        => $copyDate->note(),
899
                    open_hour    => $copyDate->open_hour(),
900
                    close_hour   => $copyDate->close_hour()
901
                });
902
903
            }
904
        }else{
905
            my $to_startDate = dt_from_string($to_startDate);
906
            my $to_endDate = dt_from_string($to_endDate);
907
            for (my $tempDate = $from_startDate->clone(); $tempDate <= $endDate;$tempDate->add(days => 1)){
908
                my $from_formatedDate = $dtf->format_datetime($tempDate);
909
                my $fromDate = $schema->resultset('DiscreteCalendar')->search(
910
                    {
911
                        branchcode  => $branchcode,
912
                        date        => $from_formatedDate,
913
                    },
914
                    {
915
                        order_by    => { -asc => 'date' }
916
                    }
917
                );
918
                my $to_formatedDate = $dtf->format_datetime($to_startDate);
919
                my $toDate = $schema->resultset('DiscreteCalendar')->search(
920
                    {
921
                        branchcode  => $branchcode,
922
                        date        => $to_formatedDate
923
                    },
924
                    {
925
                        order_by    => { -asc => 'date' }
926
                    }
927
                );
928
                my $copyDate = $fromDate->next();
929
                $toDate->next()->update({
930
                    is_opened    => $copyDate->is_opened(),
931
                    holiday_type => $copyDate->holiday_type(),
932
                    note        => $copyDate->note(),
933
                    open_hour    => $copyDate->open_hour(),
934
                    close_hour   => $copyDate->close_hour()
935
                });
936
                $to_startDate->add(days =>1);
937
            }
938
        }
939
940
941
    }
942
}
943
944
=head2 days_between
945
946
   $cal->days_between( $start_date, $end_date )
947
948
Calculates the number of days the library is opened between C<$start_date> and C<$end_date>
949
950
=cut
951
952
sub days_between {
953
    my ($self, $start_date, $end_date, ) = @_;
954
    my $branchcode = $self->{branchcode};
955
956
    if ( $start_date->compare($end_date) > 0 ) {
957
        # swap dates
958
        my $int_dt = $end_date;
959
        $end_date = $start_date;
960
        $start_date = $int_dt;
961
    }
962
963
    my $schema = Koha::Database->new->schema;
964
    my $dtf = $schema->storage->datetime_parser;
965
    $start_date = $dtf->format_datetime($start_date);
966
    $end_date = $dtf->format_datetime($end_date);
967
968
    my $days_between = $schema->resultset('DiscreteCalendar')->search(
969
        {
970
            branchcode  => $branchcode,
971
            is_opened    => 1,
972
        },
973
        {
974
            where       => \['date >= date(?) AND date < date(?)',$start_date, $end_date]
975
        }
976
    );
977
978
    return DateTime::Duration->new( days => $days_between->count());
979
}
980
981
=head2 next_open_day
982
983
   $open_date = $self->next_open_day($base_date);
984
985
Returns a string representing the next day the library is open, starting from C<$base_date>
986
987
=cut
988
989
sub next_open_day {
990
    my ( $self, $date ) = @_;
991
    my $branchcode = $self->{branchcode};
992
    my $schema = Koha::Database->new->schema;
993
    my $dtf = $schema->storage->datetime_parser;
994
    $date = $dtf->format_datetime($date);
995
996
    my $rs = $schema->resultset('DiscreteCalendar')->search(
997
        {
998
            branchcode  => $branchcode,
999
            is_opened    => 1,
1000
        },
1001
        {
1002
            where       => \['date > date(?)', $date],
1003
            order_by    => { -asc => 'date' },
1004
            rows        => 1
1005
        }
1006
    );
1007
    return dt_from_string( $rs->next()->date(), 'iso');
1008
}
1009
1010
=head2 prev_open_day
1011
1012
   $open_date = $self->prev_open_day($base_date);
1013
1014
Returns a string representing the closest previous day the library was open, starting from C<$base_date>
1015
1016
=cut
1017
1018
sub prev_open_day {
1019
    my ( $self, $date ) = @_;
1020
    my $branchcode = $self->{branchcode};
1021
    my $schema = Koha::Database->new->schema;
1022
    my $dtf = $schema->storage->datetime_parser;
1023
    $date = $dtf->format_datetime($date);
1024
1025
    my $rs = $schema->resultset('DiscreteCalendar')->search(
1026
        {
1027
            branchcode  => $branchcode,
1028
            is_opened    => 1,
1029
        },
1030
        {
1031
            where       => \['date < date(?)', $date],
1032
            order_by    => { -desc => 'date' },
1033
            rows        => 1
1034
        }
1035
    );
1036
    return dt_from_string( $rs->next()->date(), 'iso');
1037
}
1038
1039
=head2 days_forward
1040
1041
    $fwrd_date = $calendar->days_forward($start, $count)
1042
1043
Returns the date C<$count> days in the future from C<$start>, ignoring days where the library is closed.
1044
1045
=cut
1046
1047
sub days_forward {
1048
    my $self     = shift;
1049
    my $start_dt = shift;
1050
    my $num_days = shift;
1051
1052
    return $start_dt unless $num_days > 0;
1053
1054
    my $base_dt = $start_dt->clone();
1055
1056
    while ($num_days--) {
1057
        $base_dt = $self->next_open_day($base_dt);
1058
    }
1059
1060
    return $base_dt;
1061
}
1062
1063
=head2 hours_between
1064
1065
    $hours = $calendar->hours_between($start_dt, $end_dt)
1066
1067
Returns the number of hours between C<$start_dt> and C<$end_dt>. This is the imprecise
1068
version, which simply calculates the number of day times 24. To take opening hours into account
1069
see C<open_hours_between>/
1070
1071
=cut
1072
1073
sub hours_between {
1074
    my ($self, $start_dt, $end_dt) = @_;
1075
    my $branchcode = $self->{branchcode};
1076
    my $schema = Koha::Database->new->schema;
1077
    my $dtf = $schema->storage->datetime_parser;
1078
    my $start_date = $start_dt->clone();
1079
    my $end_date = $end_dt->clone();
1080
    my $duration = $end_date->delta_ms($start_date);
1081
    $start_date->truncate( to => 'day' );
1082
    $end_date->truncate( to => 'day' );
1083
1084
    # NB this is a kludge in that it assumes all days are 24 hours
1085
    # However for hourly loans the logic should be expanded to
1086
    # take into account open/close times then it would be a duration
1087
    # of library open hours
1088
    my $skipped_days = 0;
1089
    $start_date = $dtf->format_datetime($start_date);
1090
    $end_date = $dtf->format_datetime($end_date);
1091
    my $hours_between = $schema->resultset('DiscreteCalendar')->search(
1092
        {
1093
            branchcode  =>  $branchcode,
1094
            is_opened    => 0
1095
        },
1096
        {
1097
            where  => {date => {-between => [$start_date, $end_date]}},
1098
        }
1099
    );
1100
1101
    if ($skipped_days = $hours_between->count()) {
1102
        $duration->subtract_duration(DateTime::Duration->new( hours => 24 * $skipped_days));
1103
    }
1104
1105
    return $duration;
1106
}
1107
1108
=head2 open_hours_between
1109
1110
  $hours = $calendar->open_hours_between($start_date, $end_date)
1111
1112
Returns the number of hours between C<$start_date> and C<$end_date>, taking into
1113
account the opening hours of the library.
1114
1115
=cut
1116
1117
sub open_hours_between {
1118
    my ($self, $start_date, $end_date) = @_;
1119
    my $branchcode = $self->{branchcode};
1120
    my $schema = Koha::Database->new->schema;
1121
    my $dtf = $schema->storage->datetime_parser;
1122
    $start_date = $dtf->format_datetime($start_date);
1123
    $end_date = $dtf->format_datetime($end_date);
1124
1125
    my $working_hours_between = $schema->resultset('DiscreteCalendar')->search(
1126
        {
1127
            branchcode  => $branchcode,
1128
            is_opened    => 1,
1129
        },
1130
        {
1131
            select  => \['sum(time_to_sec(timediff(close_hour, open_hour)) / 3600)'],
1132
            as      => [qw /hours_between/],
1133
            where   => \['date BETWEEN DATE(?) AND DATE(?)', $start_date, $end_date]
1134
        }
1135
    );
1136
1137
    my $loan_day = $schema->resultset('DiscreteCalendar')->search(
1138
        {
1139
            branchcode  => $branchcode,
1140
        },
1141
        {
1142
            where       => \['date = DATE(?)', $start_date],
1143
        }
1144
    );
1145
1146
    my $return_day = $schema->resultset('DiscreteCalendar')->search(
1147
        {
1148
            branchcode  => $branchcode,
1149
        },
1150
        {
1151
            where       => \['date = DATE(?)', $end_date],
1152
        }
1153
    );
1154
1155
    #Capture the time portion of the date
1156
    $start_date =~ /\s(.*)/;
1157
    my $loan_date_time = $1;
1158
    $end_date =~ /\s(.*)/;
1159
    my $return_date_time = $1;
1160
1161
    my $not_used_hours = $schema->resultset('DiscreteCalendar')->search(
1162
        {
1163
            branchcode  => $branchcode,
1164
            is_opened    => 1,
1165
        },
1166
        {
1167
            select  => \[ '(time_to_sec(timediff(?, ?)) + time_to_sec(timediff(?, ?)) ) / 3600', $return_day->next()->close_hour(), $return_date_time, $loan_date_time, $loan_day->next()->open_hour()],
1168
            as      => [qw /not_used_hours/],
1169
        }
1170
    );
1171
1172
    return ($working_hours_between->next()->get_column('hours_between') - $not_used_hours->next()->get_column('not_used_hours'));
1173
}
1174
1175
=head2 addDate
1176
1177
  my $dt = $calendar->addDate($date, $dur, $unit)
1178
1179
C<$date> is a DateTime object representing the starting date of the interval.
1180
C<$offset> is a duration to add to it (DateTime::Duration objects are supported as legacy)
1181
C<$unit> is a string value 'days' or 'hours' toflag granularity of duration
1182
1183
=cut
1184
1185
sub addDate {
1186
    my ( $self, $startdate, $add_duration, $unit ) = @_;
1187
1188
    # Default to days duration (legacy support I guess)
1189
    if ( ref $add_duration ne 'DateTime::Duration' ) {
1190
        $add_duration = DateTime::Duration->new( days => $add_duration );
1191
    }
1192
1193
    $unit ||= 'days'; # default days ?
1194
    my $dt;
1195
1196
    if ( $unit eq 'hours' ) {
1197
        # Fixed for legacy support. Should be set as a branch parameter
1198
        my $return_by_hour = 10;
1199
1200
        $dt = $self->addHours($startdate, $add_duration, $return_by_hour);
1201
    } else {
1202
        # days
1203
        $dt = $self->addDays($startdate, $add_duration);
1204
    }
1205
1206
    return $dt;
1207
}
1208
1209
=head2 addHours
1210
1211
  $end = $calendar->addHours($start, $hours_duration, $return_by_hour)
1212
1213
Add C<$hours_duration> to C<$start> date.
1214
C<$return_by_hour> is an integer value representing the opening hour for the branch
1215
1216
=cut
1217
1218
sub addHours {
1219
    my ( $self, $startdate, $hours_duration, $return_by_hour ) = @_;
1220
    my $base_date = $startdate->clone();
1221
1222
    $base_date->add_duration($hours_duration);
1223
1224
    # If we are using the calendar behave for now as if Datedue
1225
    # was the chosen option (current intended behaviour)
1226
1227
    if ( $self->{days_mode} ne 'Days' &&
1228
    $self->is_holiday($base_date) ) {
1229
1230
        if ( $hours_duration->is_negative() ) {
1231
            $base_date = $self->prev_open_day($base_date);
1232
        } else {
1233
            $base_date = $self->next_open_day($base_date);
1234
        }
1235
1236
        $base_date->set_hour($return_by_hour);
1237
1238
    }
1239
1240
    return $base_date;
1241
}
1242
1243
=head2 addDays
1244
1245
  $date = $calendar->addDays($start, $duration)
1246
1247
Add C<$days_duration> to C<$start> date. If the calendar's days_mode is set
1248
to 'Calendar', it ignores closed days. Else if the calendar is set to 'Datedue'
1249
it calculates the date normally, and then pushes to result to the next open day.
1250
1251
=cut
1252
1253
sub addDays {
1254
    my ( $self, $startdate, $days_duration ) = @_;
1255
    my $base_date = $startdate->clone();
1256
1257
    $self->{days_mode} ||= q{};
1258
1259
    if ( $self->{days_mode} eq 'Calendar' ) {
1260
        # use the calendar to skip all days the library is closed
1261
        # when adding
1262
        my $days = abs $days_duration->in_units('days');
1263
1264
        if ( $days_duration->is_negative() ) {
1265
            while ($days) {
1266
                $base_date = $self->prev_open_day($base_date);
1267
                --$days;
1268
            }
1269
        } else {
1270
            while ($days) {
1271
                $base_date = $self->next_open_day($base_date);
1272
                --$days;
1273
            }
1274
        }
1275
1276
    } else { # Days or Datedue
1277
        # use straight days, then use calendar to push
1278
        # the date to the next open day if Datedue
1279
        $base_date->add_duration($days_duration);
1280
1281
        if ( $self->{days_mode} eq 'Datedue' ) {
1282
            # Datedue, then use the calendar to push
1283
            # the date to the next open day if holiday
1284
            if (!$self->is_opened($base_date) ) {
1285
1286
                if ( $days_duration->is_negative() ) {
1287
                    $base_date = $self->prev_open_day($base_date);
1288
                } else {
1289
                    $base_date = $self->next_open_day($base_date);
1290
                }
1291
            }
1292
        }
1293
    }
1294
1295
    return $base_date;
1296
}
1297
1298
1;
(-)a/Koha/Schema/Result/DiscreteCalendar.pm (+111 lines)
Line 0 Link Here
1
use utf8;
2
package Koha::Schema::Result::DiscreteCalendar;
3
4
# Created by DBIx::Class::Schema::Loader
5
# DO NOT MODIFY THE FIRST PART OF THIS FILE
6
7
=head1 NAME
8
9
Koha::Schema::Result::DiscreteCalendar
10
11
=cut
12
13
use strict;
14
use warnings;
15
16
use base 'DBIx::Class::Core';
17
18
=head1 TABLE: C<discrete_calendar>
19
20
=cut
21
22
__PACKAGE__->table("discrete_calendar");
23
24
=head1 ACCESSORS
25
26
=head2 date
27
28
  data_type: 'datetime'
29
  datetime_undef_if_invalid: 1
30
  is_nullable: 0
31
32
=head2 branchcode
33
34
  data_type: 'varchar'
35
  is_nullable: 0
36
  size: 10
37
38
=head2 is_opened
39
40
  data_type: 'tinyint'
41
  default_value: 1
42
  is_nullable: 1
43
44
=head2 holiday_type
45
46
  data_type: 'varchar'
47
  default_value: (empty string)
48
  is_nullable: 1
49
  size: 1
50
51
=head2 note
52
53
  data_type: 'varchar'
54
  default_value: (empty string)
55
  is_nullable: 1
56
  size: 30
57
58
=head2 open_hour
59
60
  data_type: 'time'
61
  is_nullable: 0
62
63
=head2 close_hour
64
65
  data_type: 'time'
66
  is_nullable: 0
67
68
=cut
69
70
__PACKAGE__->add_columns(
71
  "date",
72
  {
73
    data_type => "datetime",
74
    datetime_undef_if_invalid => 1,
75
    is_nullable => 0,
76
  },
77
  "branchcode",
78
  { data_type => "varchar", is_nullable => 0, size => 10 },
79
  "is_opened",
80
  { data_type => "tinyint", default_value => 1, is_nullable => 1 },
81
  "holiday_type",
82
  { data_type => "varchar", default_value => "", is_nullable => 1, size => 1 },
83
  "note",
84
  { data_type => "varchar", default_value => "", is_nullable => 1, size => 30 },
85
  "open_hour",
86
  { data_type => "time", is_nullable => 0 },
87
  "close_hour",
88
  { data_type => "time", is_nullable => 0 },
89
);
90
91
=head1 PRIMARY KEY
92
93
=over 4
94
95
=item * L</branchcode>
96
97
=item * L</date>
98
99
=back
100
101
=cut
102
103
__PACKAGE__->set_primary_key("branchcode", "date");
104
105
106
# Created by DBIx::Class::Schema::Loader v0.07045 @ 2017-04-19 10:07:41
107
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:wtctW8ZzCkyCZFZmmavFEw
108
109
110
# You can replace this text with custom code or comments, and it will be preserved on regeneration
111
1;
(-)a/installer/data/mysql/atomicupdate/bug_17015_part1_create_discrete_calendar.sql (+14 lines)
Line 0 Link Here
1
-- Bugzilla 17015
2
-- New koha calendar
3
-- Create discrete_calendar table to keep track of library day's information
4
5
CREATE TABLE `discrete_calendar` (
6
    `date` datetime NOT NULL,
7
    `branchcode` varchar(10) NOT NULL,
8
    `is_opened` tinyint(1) DEFAULT 1,
9
    `holiday_type` varchar(1) DEFAULT '',
10
    `note` varchar(30) DEFAULT '',
11
    `open_hour` time NOT NULL,
12
    `close_hour` time NOT NULL,
13
    PRIMARY KEY (`branchcode`,`date`)
14
);
(-)a/installer/data/mysql/atomicupdate/bug_17015_part2_fill_discrete_calendar.perl (+171 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
#
4
#   Script that fills the discrete_calendar table with dates, using the other date-related tables
5
#
6
use strict;
7
use warnings;
8
use DateTime;
9
use DateTime::Format::Strptime;
10
use Data::Dumper;
11
use Getopt::Long;
12
use C4::Context;
13
14
# Options
15
my $help = 0;
16
my $daysInFuture = 365;
17
GetOptions (
18
            'days|?|d=i' => \$daysInFuture,
19
            'help|?|h' => \$help);
20
my $usage = << 'ENDUSAGE';
21
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;
35
$dbh->{AutoCommit} = 0;
36
$dbh->{RaiseError} = 1;
37
38
my $currentDate = DateTime->today;
39
40
# two years ago
41
my $startDate = DateTime->new(
42
day       => $currentDate->day(),
43
month     => $currentDate->month(),
44
year      => $currentDate->year()-2,
45
time_zone => C4::Context->tz()
46
)->truncate( to => 'day' );
47
48
# a year into the future
49
my $endDate = DateTime->new(
50
day       => $currentDate->day(),
51
month     => $currentDate->month(),
52
year      => $currentDate->year(),
53
time_zone => C4::Context->tz()
54
)->truncate( to => 'day' );
55
$endDate->add(days => $daysInFuture);
56
57
#Added a default (standard) branch.
58
my $add_default_branch = 'INSERT IGNORE INTO branches (branchname, branchcode) VALUES(?,?)';
59
my $add_Branch_Sth = $dbh->prepare($add_default_branch);
60
$add_Branch_Sth->execute('Default', '');
61
# finds branches;
62
my $selectBranchesSt = 'SELECT branchcode FROM branches';
63
my $selectBranchesSth = $dbh->prepare($selectBranchesSt);
64
$selectBranchesSth->execute();
65
my @branches = ();
66
for my $branchCode ( $selectBranchesSth->fetchrow_array ) {
67
    print $branchCode . "\n";
68
    push @branches,$branchCode;
69
}
70
print "REACH\n";
71
print Dumper(\@branches);
72
73
74
# finds what days are closed for each branch
75
my %repeatableHolidaysPerBranch = ();
76
my %specialHolidaysPerBranch = ();
77
my $selectWeeklySt;
78
my $selectWeeklySth;
79
80
foreach my $branch (@branches){
81
82
    $selectWeeklySt = 'SELECT weekday, title, day, month FROM repeatable_holidays WHERE branchcode = ?';
83
    $selectWeeklySth = $dbh->prepare($selectWeeklySt);
84
    $selectWeeklySth->execute($branch);
85
86
    my @weeklyHolidays = ();
87
88
    while ( my ($weekDay, $title, $day, $month) = $selectWeeklySth->fetchrow_array ) {
89
        push @weeklyHolidays,{weekday => $weekDay, title => $title, day => $day, month => $month};
90
91
    }
92
93
    $repeatableHolidaysPerBranch{$branch} = \@weeklyHolidays;
94
95
    my $selectSpecialHolidayDateSt = 'SELECT day,month,year,title FROM special_holidays WHERE branchcode = ? AND isexception = 0';
96
    my $specialHolidayDatesSth = $dbh->prepare($selectSpecialHolidayDateSt);
97
    $specialHolidayDatesSth -> execute($branch);
98
    # Tranforms dates from specialHolidays table in DateTime for our new table
99
    my @specialHolidayDates = ();
100
    while ( my ($day, $month, $year, $title) = $specialHolidayDatesSth->fetchrow_array ) {
101
102
        my $specialHolidayDate = DateTime->new(
103
        day       => $day,
104
        month     => $month,
105
        year      => $year,
106
        time_zone => C4::Context->tz()
107
        )->truncate( to => 'day' );
108
        push @specialHolidayDates,{date=>$specialHolidayDate, title=> $title};
109
    }
110
111
    $specialHolidaysPerBranch{$branch} = \@specialHolidayDates;
112
}
113
# Fills table with dates and sets 'is_opened' according to the branch's weekly restrictions (repeatable_holidays)
114
my $insertDateSt;
115
my $insertDateSth;
116
117
# Loop that does everything in the world
118
for (my $tempDate = $startDate->clone(); $tempDate <= $endDate;$tempDate->add(days => 1)){
119
    foreach my $branch (@branches){
120
        my $dayOfWeek = $tempDate->day_of_week;
121
        # Representation fix
122
        # DateTime object dow (1-7) where Monday is 1
123
        # Arrays are 0-based where 0 = Sunday, not 7.
124
        $dayOfWeek -=1 unless $dayOfWeek ==7;
125
        $dayOfWeek =0 if $dayOfWeek ==7;
126
127
        my $open_hour = "09:00:00";
128
        my $close_hour = "17:00:00";
129
130
        # Finds closed days
131
        my $is_opened =1;
132
        my $specialDescription = "";
133
        my $holiday_type ='';
134
        $dayOfWeek = $tempDate->day_of_week;
135
        foreach my $holidayWeekDay (@{$repeatableHolidaysPerBranch{$branch}}){
136
            if($holidayWeekDay->{weekday} && $dayOfWeek == $holidayWeekDay->{weekday}){
137
                $is_opened = 0;
138
                $specialDescription = $holidayWeekDay->{title};
139
                $holiday_type = 'W';
140
            }elsif($holidayWeekDay->{day} && $holidayWeekDay->{month}){
141
                my $date = DateTime->new(
142
                day       => $holidayWeekDay->{day},
143
                month     => $holidayWeekDay->{month},
144
                year      => $tempDate->year(),
145
                time_zone => C4::Context->tz()
146
                )->truncate( to => 'day' );
147
148
                if ($tempDate == $date) {
149
                    $is_opened = 0;
150
                    $specialDescription = $holidayWeekDay->{title};
151
                    $holiday_type = 'R';
152
                }
153
            }
154
        }
155
156
        foreach my $specialDate (@{$specialHolidaysPerBranch{$branch}}){
157
            if($tempDate->datetime() eq $specialDate->{date}->datetime() ){
158
                $is_opened = 0;
159
                $specialDescription = $specialDate->{title};
160
                $holiday_type = 'E';
161
            }
162
        }
163
        #final insert statement
164
165
        $insertDateSt = 'INSERT INTO discrete_calendar (date,branchcode,is_opened,holiday_type,note,open_hour,close_hour) VALUES (?,?,?,?,?,?,?)';
166
        $insertDateSth = $dbh->prepare($insertDateSt);
167
        $insertDateSth->execute($tempDate,$branch,$is_opened,$holiday_type,$specialDescription,$open_hour,$close_hour);
168
    }
169
}
170
# If everything went well we commit to the database
171
$dbh->commit();
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/discrete_calendar.tt (+787 lines)
Line 0 Link Here
1
[% USE Branches %]
2
[% INCLUDE 'doc-head-open.inc' %]
3
<title>Koha &rsaquo; Tools &rsaquo; [% Branches.GetName( branch ) %] calendar</title>
4
[% INCLUDE 'doc-head-close.inc' %]
5
[% INCLUDE 'calendar.inc' %]
6
<link rel="stylesheet" type="text/css" href="[% interface %]/[% theme %]/css/datatables.css" />
7
<script type="text/javascript" src="[% interface %]/lib/jquery/plugins/jquery-ui-timepicker-addon.min.js"></script>
8
[% INCLUDE 'datatables.inc' %]
9
    <script type="text/javascript">
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
24
        /**
25
        * Displays the details of the selected date on a side panel
26
        */
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
42
            $('.newHoliday #title').val(title);
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
49
            //This changes the label of the date type on the edit panel
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
64
            //Select the correct holiday type on the dropdown menu
65
            if (datesInfos[dateString].holiday_type !=''){
66
                var type = datesInfos[dateString].holiday_type;
67
                $('#holidayType option[value="'+ type +'"]').attr('selected', true)
68
            }else{
69
                $('#holidayType option[value="none"]').attr('selected', true)
70
            }
71
72
            //If it is a weekly or repeatable holiday show the option to delete the type
73
            if(datesInfos[dateString].holiday_type == 'W' || datesInfos[dateString].holiday_type == 'R'){
74
                $('#deleteType').show("fast");
75
            }else{
76
                $('#deleteType').hide("fast");
77
            }
78
79
            //This value is to disable and hide input when the date is in the past, because you can't edit it.
80
            var value = false;
81
            var today = new Date();
82
            today.setHours(0,0,0,0);
83
            if(date_obj < today ){
84
                $("#holtype").attr("class","key past-date").html(_("Past date"));
85
                $("#CopyRadioButton").attr("checked", "checked");
86
                value = true;
87
                $(".CopyDatePanel").toggle(value);
88
            }
89
            $("#title").prop('disabled', value);
90
            $("#holidayType select").prop('disabled', value);
91
            $("#openHour").prop('disabled', value);
92
            $("#closeHour").prop('disabled', value);
93
            $("#EditRadioButton").parent().toggle(!value);
94
95
        }
96
97
        function hidePanel(aPanelName) {
98
            $("#"+aPanelName).slideUp("fast");
99
        }
100
101
        function changeBranch () {
102
            var branch = $("#branch option:selected").val();
103
            location.href='/cgi-bin/koha/tools/discrete_calendar.pl?branch=' + branch + '&calendardate=' + "[% calendardate %]";
104
        }
105
106
        function Help() {
107
            newin=window.open("/cgi-bin/koha/help.pl","KohaHelp",'width=600,height=600,toolbar=false,scrollbars=yes');
108
        }
109
110
        // This function gives css clases to each kind of day
111
        function dateStatusHandler(date) {
112
            date = getSeparetedDate(date);
113
            var day = date.day;
114
            var month = date.month;
115
            var year = date.year;
116
            var weekDay = date.weekDay;
117
            var dayName = weekdays[weekDay];
118
            var dateString = date.dateString;
119
            var today = new Date();
120
            today.setHours(0,0,0,0);
121
122
            if (datesInfos[dateString] && datesInfos[dateString].holiday_type =='W'){
123
                return [true, "repeatableweekly", _("Weekly holiday: %s").format(datesInfos[dateString].title)];
124
            } else if (datesInfos[dateString] && datesInfos[dateString].holiday_type == 'R') {
125
                return [true, "repeatableyearly", _("Yearly holiday: %s").format(datesInfos[dateString].title)];
126
            } else if (datesInfos[dateString] && datesInfos[dateString].holiday_type == 'N') {
127
                return [true, "exception", _("Need validation: %s").format(datesInfos[dateString].title)];
128
            } else if (datesInfos[dateString] && datesInfos[dateString].holiday_type == 'F') {
129
                return [true, "float", _("Floating holiday: %s").format(datesInfos[dateString].title)];
130
            } else if (datesInfos[dateString] && datesInfos[dateString].holiday_type == 'E') {
131
                return [true, "holiday", _("Single holiday: %s").format(datesInfos[dateString].title)];
132
            } else {
133
                if(date.date_obj < today ){
134
                    return [true, "past-date", _("Past day")];
135
                }else{
136
                    return [true, "normalday", _("Normal day")];
137
                }
138
            }
139
        }
140
141
        /* This function is in charge of showing the correct panel considering the kind of holiday */
142
        function dateChanged(text, date) {
143
            date = getSeparetedDate(date);
144
            var day = date.day;
145
            var month = date.month;
146
            var year = date.year;
147
            var weekDay = date.weekDay;
148
            var dayName = weekdays[weekDay];
149
            var dateString = date.dateString;
150
            var date_obj = date.date_obj;
151
            //set value of form hidden field
152
            $('#from_copyFrom').val(text);
153
154
            showHoliday(date_obj, dateString, dayName, day, month, year, weekDay, datesInfos[dateString].title, datesInfos[dateString].holiday_type);
155
        }
156
157
        /**
158
        * This function separate a given date object a returns an array containing all needed information about the date.
159
        */
160
        function getSeparetedDate(date){
161
            var mydate = new Array();
162
            var day = (date.getDate() < 10 ? '0' : '') + date.getDate();
163
            var month = ((date.getMonth()+1) < 10 ? '0' : '') + (date.getMonth() +1);
164
            var year = date.getFullYear();
165
            var weekDay = date.getDay();
166
            //iso date string
167
            var dateString = year + '-' + month + '-' + day;
168
            mydate = {
169
                date_obj : date,
170
                dateString : dateString,
171
                weekDay: weekDay,
172
                year: year,
173
                month: month,
174
                day: day
175
            };
176
177
            return mydate;
178
        }
179
180
        /**
181
        * Valide the forms before send them to the backend
182
        */
183
        function validateForm(form){
184
            if(form =='newHoliday' && $('#CopyRadioButton').is(':checked')){
185
                if($('#to_copyFromDatePicker').val() =='' || $('#to_copyToDatePicker').val() ==''){
186
                    alert("You have to pick a FROM and TO in the Copy to different dates.");
187
                    return false;
188
                }else if ($('#from_copyToDatePicker').val()){
189
                    var from_DateFrom = new Date($("#jcalendar-container").datepicker("getDate"));
190
                    var from_DateTo = new Date($('#from_copyToDatePicker').datepicker("getDate"));
191
                    var to_DateFrom = new Date($('#to_copyFromDatePicker').datepicker("getDate"));
192
                    var to_DateTo = new Date($('#to_copyToDatePicker').datepicker("getDate"));
193
194
                    var from_start = Math.round( from_DateFrom.getTime() / (3600*24*1000)); //days as integer from..
195
                    var from_end   = Math.round( from_DateTo.getTime() / (3600*24*1000));
196
                    var to_start = Math.round( to_DateFrom.getTime() / (3600*24*1000));
197
                    var to_end   = Math.round( to_DateTo.getTime() / (3600*24*1000));
198
199
                    var from_daysDiff = from_end - from_start +1;
200
                    var to_daysDiff = to_end - to_start + 1;
201
                    if(from_daysDiff == to_daysDiff){
202
                        $('#daysnumber').val(to_daysDiff);
203
                        return true;
204
                    }else{
205
                        alert("You have to pick the same number of days if you choose 2 ranges");
206
                        return false;
207
                    }
208
                }
209
            }else if(form == 'CopyCalendar'){
210
                if ($('#newBranch').val() ==''){
211
                    alert("Please select a copy to calendar.");
212
                    return false;
213
                }else{
214
                    return true;
215
                }
216
            }else {
217
                return true;
218
            }
219
        }
220
221
        function go_to_date(isoDate){
222
            //I added the time to get around the timezone
223
            var date = getSeparetedDate(new Date(isoDate + " 00:00:00"));
224
            var day = date.day;
225
            var month = date.month;
226
            var year = date.year;
227
            var weekDay = date.weekDay;
228
            var dayName = weekdays[weekDay];
229
            var dateString = date.dateString;
230
            var date_obj = date.date_obj;
231
232
            $("#jcalendar-container").datepicker("setDate", date_obj);
233
            showHoliday(date_obj, dateString, dayName, day, month, year, weekDay, datesInfos[dateString].title, datesInfos[dateString].holiday_type);
234
        }
235
236
        /**
237
        *Check if date range have the same opening, closing hours and holiday type if there's one.
238
        */
239
        function checkRange(date){
240
            date = new Date(date);
241
            $('#toDate').val(getSeparetedDate(date).dateString);
242
            var fromDate = new Date($("#jcalendar-container").datepicker("getDate"));
243
            var sameHoliday =true;
244
            var sameOpenHours =true;
245
            var sameCloseHours =true;
246
247
            $('#days_of_week option[value="everyday"]').attr('selected', true);
248
            for (var i = fromDate; i <= date ; i.setDate(i.getDate() + 1)) {
249
                var myDate1 = getSeparetedDate(i);
250
                var date1 = myDate1.dateString;
251
                var holidayType1 = datesInfos[date1].holiday_type;
252
                var open_hours1 = datesInfos[date1].open_hour;
253
                var close_hours1 = datesInfos[date1].close_hour;
254
                for (var j = fromDate; j <= date ; j.setDate(j.getDate() + 1)) {
255
                    var myDate2 = getSeparetedDate(j);
256
                    var date2 = myDate2.dateString;
257
                    var holidayType2 = datesInfos[date2].holiday_type;
258
                    var open_hours2 = datesInfos[date2].open_hour;
259
                    var close_hours2 = datesInfos[date2].close_hour;
260
261
                    if (sameHoliday && holidayType1 != holidayType2){
262
                        $('#holidayType option[value="empty"]').attr('selected', true);
263
                        sameHoliday=false;
264
                    }
265
                    if(sameOpenHours && (open_hours1 != open_hours2)){
266
                        $('#openHour').val('');
267
                        sameOpenHours=false;
268
                    }
269
                    if(sameCloseHours && (close_hours1 != close_hours2)){
270
                        $('#closeHour').val('');
271
                        sameCloseHours=false;
272
                    }
273
                }
274
                if (!sameOpenHours && !sameCloseHours && !sameHoliday){
275
                    return false;
276
                }
277
            }
278
            return true;
279
        }
280
281
        $(document).ready(function() {
282
            $(".hint").hide();
283
            $("#branch").change(function(){
284
                changeBranch();
285
            });
286
            $("#holidayweeklyrepeatable>tbody>tr").each(function(){
287
                var first_td = $(this).find('td').first();
288
                first_td.html(weekdays[first_td.html()]);
289
            });
290
            $("a.helptext").click(function(){
291
                $(this).parent().find(".hint").toggle(); return false;
292
            });
293
            //Set the correct coloring, default date and the date ranges for all datepickers
294
            $.datepicker.setDefaults({
295
                beforeShowDay: function(thedate) {
296
                    return dateStatusHandler(thedate);
297
                },
298
                defaultDate: new Date("[% keydate %]"),
299
                minDate: new Date("[% minDate %]"),
300
                maxDate: new Date("[% maxDate %]")
301
            });
302
            //Main datepicker
303
            $("#jcalendar-container").datepicker({
304
                onSelect: function(dateText, inst) {
305
                    dateChanged(dateText, $(this).datepicker("getDate"));
306
                },
307
            });
308
            $('#from_copyToDatePicker').datepicker();
309
            $("#from_copyToDatePicker").change(function(){
310
                checkRange($(this).datepicker("getDate"));
311
                $('#from_copyTo').val(($(this).val()));
312
                if($('#from_copyToDatePicker').val()){
313
                    $('#days_of_week').show("fast");
314
                }else{
315
                    $('#days_of_week').hide("fast");
316
                }
317
            });
318
            //Datepickers for copy dates feature
319
            $('#to_copyFromDatePicker').datepicker();
320
            $("#to_copyFromDatePicker").change(function(){
321
                $('#to_copyFrom').val(($(this).val()));
322
            });
323
            $('#to_copyToDatePicker').datepicker();
324
            $("#to_copyToDatePicker").change(function(){
325
                $('#to_copyTo').val(($(this).val()));
326
            });
327
            //Timepickers for open and close hours
328
            $('#openHour').timepicker({
329
                showOn : 'focus',
330
                timeFormat: 'HH:mm:ss',
331
                showSecond: false,
332
                stepMinute: 5,
333
            });
334
            $('#closeHour').timepicker({
335
                showOn : 'focus',
336
                timeFormat: 'HH:mm:ss',
337
                showSecond: false,
338
                stepMinute: 5,
339
            });
340
341
            $('.newHoliday input[type="radio"]').click(function () {
342
                if ($(this).attr("id") == "CopyRadioButton") {
343
                    $(".CopyToBranchPanel").hide('fast');
344
                    $(".CopyDatePanel").show('fast');
345
                } else if ($(this).attr("id") == "CopyToBranchRadioButton"){
346
                    $(".CopyDatePanel").hide('fast');
347
                    $(".CopyToBranchPanel").show('fast');
348
                } else{
349
                    $(".CopyDatePanel").hide('fast');
350
                    $(".CopyToBranchPanel").hide('fast');
351
                }
352
            });
353
354
            $(".hidePanel").on("click",function(){
355
                if( $(this).hasClass("showHoliday") ){
356
                    hidePanel("showHoliday");
357
                }if ($(this).hasClass('newHoliday')) {
358
                    hidePanel("newHoliday");
359
                }else {
360
                    hidePanel("copyHoliday");
361
                }
362
            });
363
364
            $("#deleteType_checkbox").on("change", function(){
365
                if($("#deleteType_checkbox").is(':checked')){
366
                    $('#holidayType option[value="none"]').attr('selected', true);
367
                }
368
            });
369
            $("#holidayType select").on("change", function(){
370
                if($("#holidayType select").val() == "R"){
371
                    $('#days_of_week').hide("fast");
372
                }else if ($('#from_copyToDatePicker').val()){
373
                    $('#days_of_week').show("fast");
374
                }
375
            });
376
        });
377
    //]]>
378
    </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 %]
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' %]
(-)a/misc/cronjobs/add_days_discrete_calendar.pl (+129 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
#
4
#   This script adds one day into discrete_calendar table based on the same day from the week before
5
#
6
use strict;
7
use warnings;
8
use DateTime;
9
use DateTime::Format::Strptime;
10
use Data::Dumper;
11
use Getopt::Long;
12
use C4::Context;
13
use Koha::Database;
14
use Koha::DiscreteCalendar;
15
16
# Options
17
my $help = 0;
18
my $daysInFuture = 1;
19
my $debug = 0;
20
GetOptions (
21
    'help|?|h'  => \$help,
22
    'n=i'       => \$daysInFuture,
23
    'd|?|debug' => \$debug);
24
25
my $usage = << 'ENDUSAGE';
26
27
This script adds days into discrete_calendar table based on the same day from the week before.
28
29
Examples :
30
    The latest date on discrete_calendar is : 28-07-2017
31
    The current date : 01-08-2016
32
    The dates that will be added are : 29-07-2017, 30-07-2017, 31-07-2017, 01-08-2017
33
Open close exemples :
34
    Date added is : 29-07-2017
35
    Opening/closing hours will be base on : 22-07-2017 (- 7 days)
36
    Library open or closed will be based on : 29-07-2017 (- 1 year)
37
This script has the following parameters:
38
    -h --help: this message
39
    -n : number of days to add in the futre, default : 1
40
    -d --debug: displays all added days and errors if there is any
41
42
ENDUSAGE
43
44
if ($help) {
45
    print $usage;
46
    exit;
47
}
48
49
my $schema = Koha::Database->new->schema;
50
$schema->storage->txn_begin;
51
my $dbh = C4::Context->dbh;
52
53
# Predeclaring variables that will be used several times in the code
54
my $query;
55
my $statement;
56
57
#getting the all the branches
58
$query = 'SELECT branchcode FROM discrete_calendar GROUP BY branchcode';
59
$statement = $dbh->prepare($query);
60
$statement->execute();
61
my @branches = ();
62
for my $branchcode ($statement->fetchrow_array ) {
63
    push @branches,$branchcode;
64
}
65
66
#get the latest date in the table
67
$query = "SELECT MAX(date) FROM discrete_calendar";
68
$statement = $dbh->prepare($query);
69
$statement->execute();
70
my $latestedDate = $$statement->fetchrow_array;
71
my $parser = DateTime::Format::Strptime->new(
72
    pattern => '%Y-%m-%d %H:%M:%S',
73
    on_error => 'croak',
74
);
75
76
$latestedDate = $parser->parse_datetime($latestedDate);
77
78
my $newDay = $latestedDate->clone();
79
$latestedDate->add(days => $daysInFuture);
80
81
for ($newDay->add(days => 1); $newDay <= $latestedDate; $newDay->add(days => 1)) {
82
    my $lastWeekDay = $newDay->clone();
83
    $lastWeekDay->add(days=> -8);
84
    my $dayOfWeek = $lastWeekDay->day_of_week;
85
    # Representation fix
86
    # DateTime object dow (1-7) where Monday is 1
87
    # Arrays are 0-based where 0 = Sunday, not 7.
88
    $dayOfWeek -= 1 unless $dayOfWeek == 7;
89
    $dayOfWeek = 0 if $dayOfWeek == 7;
90
91
    #checking if it was open on the same day from last year
92
    my $yearAgo = $newDay->clone();
93
    $yearAgo = $yearAgo->add(years => -1);
94
    my $last_year = 'SELECT is_opened, holiday_type, note FROM discrete_calendar WHERE date=? AND branchcode=?';
95
    my $day_last_week = "SELECT open_hour, close_hour FROM discrete_calendar WHERE DAYOFWEEK(date)=DAYOFWEEK(?) and date < ? order by date desc limit 1";
96
    my $add_Day = 'INSERT INTO discrete_calendar (date,branchcode,is_opened,open_hour,close_hour) VALUES (?,?,?,?,?)';
97
    my $note ='';
98
    #insert into discrete_calendar for each branch
99
    foreach my $branchCode(@branches){
100
        $$statement = $dbh->prepare($last_year);
101
        $$statement->execute($yearAgo,$branchCode);
102
        my ($is_opened, $holiday_type, $note) = $$statement->fetchrow_array;
103
        #weekly and unique holidays are not replicated in the future
104
        if ($holiday_type && $holiday_type ne "R"){
105
            $is_opened = 1;
106
            if ($holiday_type eq "W" || $holiday_type eq "E"){
107
                $holiday_type='';
108
                $note='';
109
            }elsif ($holiday_type eq "F"){
110
                $holiday_type = 'N';
111
            }
112
        }
113
        $holiday_type = '' if $is_opened;
114
        $$statement = $dbh->prepare($day_last_week);
115
        $$statement->execute($newDay, $newDay);
116
        my ($open_hour,$close_hour ) = $$statement->fetchrow_array;
117
        my $add_Day = 'INSERT INTO discrete_calendar (date,branchcode,is_opened,holiday_type, note,open_hour,close_hour) VALUES (?,?,?,?,?,?,?)';
118
        $$statement = $dbh->prepare($add_Day);
119
        $$statement->execute($newDay,$branchCode,$is_opened,$holiday_type,$note, $open_hour,$close_hour);
120
121
        if($debug && !$@){
122
            warn "Added day $newDay to $branchCode is opened : $is_opened, holiday_type : $holiday_type, note: $note, open_hour : $open_hour, close_hour : $close_hour \n";
123
        }elsif($@){
124
            warn "Failed to add day $newDay to $branchCode : $_\n";
125
        }
126
    }
127
}
128
# If everything went well we commit to the database
129
$schema->storage->txn_commit;
(-)a/tools/discrete_calendar.pl (-1 / +154 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
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
#####Sets holiday periods for each branch. Datedues will be extended if branch is closed -TG
19
use strict;
20
use warnings;
21
22
use CGI qw ( -utf8 );
23
24
use C4::Auth;
25
use C4::Output;
26
27
use Koha::DateUtils;
28
use Koha::DiscreteCalendar;
29
30
my $input = new CGI;
31
32
# Get the template to use
33
my ($template, $loggedinuser, $cookie)
34
    = get_template_and_user({template_name => "tools/discrete_calendar.tt",
35
                             type => "intranet",
36
                             query => $input,
37
                             authnotrequired => 0,
38
                             flagsrequired => {tools => 'edit_calendar'},
39
                             debug => 1,
40
                           });
41
42
my $branch = $input->param('branch') || C4::Context->userenv->{'branch'};
43
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
45
my $no_branch_selected = $calendar->{no_branch_selected};
46
47
my $weekday = $input->param('day_of_week');
48
49
my $holiday_type = $input->param('holidayType');
50
my $allbranches = $input->param('allBranches');
51
52
my $title = $input->param('Title');
53
54
my $action = $input->param('action') || '';
55
56
# calendardate - date passed in url for human readability (syspref)
57
# if the url has an invalid date default to 'now.'
58
my $calendarinput_dt = eval { dt_from_string( scalar $input->param('calendardate')); } || dt_from_string;
59
my $calendardate = output_pref( { dt => $calendarinput_dt, dateonly => 1 } );
60
61
if($action eq 'copyBranch'){
62
    $calendar->copy_to_branch(scalar $input->param('newBranch'));
63
} elsif($action eq 'copyDates'){
64
    my $from_startDate = $input->param('from_copyFrom') ||'';
65
    my $from_endDate = $input->param('toDate') || '';
66
    my $to_startDate = $input->param('to_copyFrom') || '';
67
    my $to_endDate = $input->param('to_copyTo') || '';
68
    my $daysnumber= $input->param('daysnumber');
69
70
    $from_startDate = dt_from_string(scalar $from_startDate) if$from_startDate  ne '';
71
    $to_startDate = dt_from_string(scalar $to_startDate) if $to_startDate ne '';
72
    $to_startDate = dt_from_string(scalar $to_startDate) if $to_startDate ne '';
73
    $to_endDate = dt_from_string(scalar $to_endDate) if $to_endDate ne '';
74
75
    $calendar->copy_holiday($from_startDate, $from_endDate, $to_startDate, $to_endDate, $daysnumber);
76
} elsif($action eq 'edit'){
77
    my $openHour = $input->param('openHour');
78
    my $closeHour = $input->param('closeHour');
79
    my $endDate = $input->param('toDate');
80
    my $deleteType = $input->param('deleteType') || 0;
81
    #Get today from javascript for a precise local time
82
    my $local_today = dt_from_string( $input->param('local_today'), 'iso');
83
84
    my $startDate = dt_from_string(scalar $input->param('from_copyFrom'));
85
86
    if($endDate ne '' ) {
87
        $endDate = dt_from_string(scalar $endDate);
88
    } else{
89
        $endDate = $startDate->clone();
90
    }
91
92
    $calendar->edit_holiday( {
93
        title        => $title,
94
        weekday      => $weekday,
95
        holiday_type => $holiday_type,
96
        open_hour    => $openHour,
97
        close_hour   => $closeHour,
98
        start_date   => $startDate,
99
        end_date     => $endDate,
100
        delete_type  => $deleteType,
101
        today        => $local_today
102
    });
103
}
104
105
# keydate - date passed to calendar.js.  calendar.js does not process dashes within a date.
106
107
my $keydate = output_pref( { dt => $calendarinput_dt, dateonly => 1, dateformat => 'iso' } );
108
$keydate =~ s/-/\//g;
109
110
# Set all the branches.
111
if ( C4::Context->only_my_library ) {
112
    $branch = C4::Context->userenv->{'branch'};
113
}
114
115
# Get all the holidays
116
117
#discrete_calendar weekly holidays
118
my @week_days = $calendar->get_week_days_holidays();
119
120
#discrete_calendar repeatable holidays
121
my @repeatable_holidays = $calendar->get_repeatable_holidays();
122
123
#discrete_calendar unique holidays
124
my @unique_holidays =$calendar->get_unique_holidays();
125
#discrete_calendar floating holidays
126
my @float_holidays =$calendar->get_float_holidays();
127
#discrete_caledar need validation holidays
128
my @need_validation_holidays =$calendar->get_need_validation_holidays();
129
130
#Calendar maximum date
131
my $minDate = $calendar->get_min_date($branch);
132
133
#Calendar minimum date
134
my $maxDate = $calendar->get_max_date($branch);
135
136
my @datesInfos = $calendar->get_dates_info($branch);
137
138
$template->param(
139
    UNIQUE_HOLIDAYS          => \@unique_holidays,
140
    FLOAT_HOLIDAYS           => \@float_holidays,
141
    NEED_VALIDATION_HOLIDAYS => \@need_validation_holidays,
142
    REPEATABLE_HOLIDAYS      => \@repeatable_holidays,
143
    WEEKLY_HOLIDAYS          => \@week_days,
144
    calendardate             => $calendardate,
145
    keydate                  => $keydate,
146
    branch                   => $branch,
147
    minDate                  => $minDate,
148
    maxDate                  => $maxDate,
149
    datesInfos               => \@datesInfos,
150
    no_branch_selected       => $no_branch_selected,
151
);
152
153
# Shows the template with the real values replaced
154
output_html_with_http_headers $input, $cookie, $template->output;

Return to bug 17015