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

(-)a/Koha/DiscreteCalendar.pm (+974 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
sub new {
33
    my ( $classname, %options ) = @_;
34
    my $self = {};
35
    bless $self, $classname;
36
    for my $o_name ( keys %options ) {
37
        my $o = lc $o_name;
38
        $self->{$o} = $options{$o_name};
39
    }
40
    if ( !defined $self->{branchcode} ) {
41
        croak 'No branchcode argument passed to Koha::DiscreteCalendar->new';
42
    }
43
    $self->_init();
44
45
    return $self;
46
}
47
48
sub _init {
49
    my $self = shift;
50
    $self->{days_mode} = C4::Context->preference('useDaysMode');
51
    #If the branchcode doesn't exist we use the default calendar.
52
    my $schema = Koha::Database->new->schema;
53
    my $branchcode = $self->{branchcode};
54
    my $dtf = $schema->storage->datetime_parser;
55
    my $today = $dtf->format_datetime(DateTime->today);
56
    my $rs = $schema->resultset('DiscreteCalendar')->single(
57
        {
58
            branchcode  => $branchcode,
59
            date        => $today
60
        }
61
    );
62
    #use default if no calendar is found
63
    if (!$rs){
64
        $self->{branchcode} = 'DFLT';
65
        $self->{no_branch_selected} = 1;
66
    }
67
68
}
69
70
sub getDatesInfo {
71
    my $self = shift;
72
    my $branchcode = $self->{branchcode};
73
    my @datesInfos =();
74
    my $schema = Koha::Database->new->schema;
75
76
    my $rs = $schema->resultset('DiscreteCalendar')->search(
77
        {
78
            branchcode  => $branchcode
79
        },
80
        {
81
            select  => [ 'date', { DATE => 'date' } ],
82
            as      => [qw/ date date /],
83
            columns =>[ qw/ holidaytype openhour closehour note/]
84
        },
85
    );
86
87
    while (my $date = $rs->next()){
88
        my $outputdate = dt_from_string( $date->date(), 'iso');
89
        $outputdate = output_pref( { dt => $outputdate, dateonly => 1 } );
90
        push @datesInfos, {
91
            date        => $date->date(),
92
            outputdate  => $outputdate,
93
            holidaytype => $date->holidaytype() ,
94
            openhour    => $date->openhour(),
95
            closehour   => $date->closehour(),
96
            note        => $date->note()
97
        };
98
    }
99
100
    return @datesInfos;
101
}
102
#This methode will copy everything from a given branch found to the new branch
103
sub add_new_branch {
104
    my ($self, $copyBranch, $newBranch) = @_;
105
    $copyBranch = 'DFLT' unless $copyBranch;
106
    my $schema = Koha::Database->new->schema;
107
108
    my $branch_rs = $schema->resultset('DiscreteCalendar')->search({
109
            branchcode => $copyBranch
110
    });
111
112
    while(my $row = $branch_rs->next()){
113
        $schema->resultset('DiscreteCalendar')->create({
114
            date        => $row->date(),
115
            branchcode  => $newBranch,
116
            isopened    => $row->isopened(),
117
            holidaytype => $row->holidaytype(),
118
            openhour    => $row->openhour(),
119
            closehour   => $row->closehour(),
120
        });
121
    }
122
123
}
124
#DiscreteCalendar data transfer object (DTO)
125
sub get_date_info {
126
    my ($self, $date) = @_;
127
    my $branchcode = $self->{branchcode};
128
    my $schema = Koha::Database->new->schema;
129
    my $dtf = $schema->storage->datetime_parser;
130
    #String dates for Database usage
131
    my $date_string = $dtf->format_datetime($date);
132
133
    my $rs = $schema->resultset('DiscreteCalendar')->search(
134
        {
135
            branchcode  => $branchcode,
136
        },
137
        {
138
            select  => [ 'date', { DATE => 'date' } ],
139
            as      => [qw/ date date /],
140
            where   => \['DATE(?) = date', $date_string ],
141
            columns =>[ qw/ branchcode holidaytype openhour closehour note/]
142
        },
143
    );
144
    my $dateDTO;
145
    while (my $date = $rs->next()){
146
        $dateDTO = {
147
            date        => $date->date(),
148
            branchcode  => $date->branchcode(),
149
            holidaytype => $date->holidaytype() ,
150
            openhour    => $date->openhour(),
151
            closehour   => $date->closehour(),
152
            note        => $date->note()
153
        };
154
    }
155
156
    return $dateDTO;
157
}
158
159
160
sub getMaxDate {
161
    my $self       = shift;
162
    my $branchcode     = $self->{branchcode};
163
    my $schema = Koha::Database->new->schema;
164
165
    my $rs = $schema->resultset('DiscreteCalendar')->search(
166
        {
167
            branchcode  => $branchcode
168
        },
169
        {
170
            select => [{ MAX => 'date' } ],
171
            as     => [qw/ max /],
172
        }
173
    );
174
175
    return $rs->next()->get_column('max');
176
}
177
178
sub getMinDate {
179
    my $self       = shift;
180
    my $branchcode     = $self->{branchcode};
181
    my $schema = Koha::Database->new->schema;
182
183
    my $rs = $schema->resultset('DiscreteCalendar')->search(
184
        {
185
            branchcode  => $branchcode
186
        },
187
        {
188
            select => [{ MIN => 'date' } ],
189
            as     => [qw/ min /],
190
        }
191
    );
192
193
    return $rs->next()->get_column('min');
194
}
195
196
197
sub get_unique_holidays {
198
    my $self = shift;
199
    my $branchcode = $self->{branchcode};
200
    my @single_holidays;
201
    my $schema = Koha::Database->new->schema;
202
203
    my $rs = $schema->resultset('DiscreteCalendar')->search(
204
        {
205
            branchcode  => $branchcode,
206
            holidaytype => 'E'
207
        },
208
        {
209
            select => [{ DATE => 'date' }, 'note' ],
210
            as     => [qw/ date note/],
211
        }
212
    );
213
    while (my $date = $rs->next()){
214
        my $outputdate = dt_from_string($date->date(), 'iso');
215
        $outputdate = output_pref( { dt => $outputdate, dateonly => 1 } );
216
        push @single_holidays, {
217
            date =>  $date->date(),
218
            outputdate => $outputdate,
219
            note => $date->note()
220
        }
221
    }
222
223
    return @single_holidays;
224
}
225
226
sub get_float_holidays {
227
    my $self = shift;
228
    my $branchcode = $self->{branchcode};
229
    my @float_holidays;
230
    my $schema = Koha::Database->new->schema;
231
232
    my $rs = $schema->resultset('DiscreteCalendar')->search(
233
        {
234
            branchcode  => $branchcode,
235
            holidaytype => 'F'
236
        },
237
        {
238
            select => [{ DATE => 'date' }, 'note' ],
239
            as     => [qw/ date note/],
240
        }
241
    );
242
    while (my $date = $rs->next()){
243
        my $outputdate = dt_from_string($date->date(), 'iso');
244
        $outputdate = output_pref( { dt => $outputdate, dateonly => 1 } );
245
        push @float_holidays, {
246
            date        =>  $date->date(),
247
            outputdate  => $outputdate,
248
            note        => $date->note()
249
        }
250
    }
251
252
    return @float_holidays;
253
}
254
255
sub get_need_valdation_holidays {
256
    my $self = shift;
257
    my $branchcode = $self->{branchcode};
258
    my @need_validation_holidays;
259
    my $schema = Koha::Database->new->schema;
260
261
    my $rs = $schema->resultset('DiscreteCalendar')->search(
262
        {
263
            branchcode  => $branchcode,
264
            holidaytype => 'N'
265
        },
266
        {
267
            select => [{ DATE => 'date' }, 'note' ],
268
            as     => [qw/ date note/],
269
        }
270
    );
271
    while (my $date = $rs->next()){
272
        my $outputdate = dt_from_string($date->date(), 'iso');
273
        $outputdate = output_pref( { dt => $outputdate, dateonly => 1 } );
274
        push @need_validation_holidays, {
275
            date        =>  $date->date(),
276
            outputdate  => $outputdate,
277
            note        => $date->note()
278
        }
279
    }
280
281
    return @need_validation_holidays;
282
}
283
284
sub get_repeatable_holidays {
285
    my $self = shift;
286
    my $branchcode = $self->{branchcode};
287
    my @repeatable_holidays;
288
    my $schema = Koha::Database->new->schema;
289
290
    my $rs = $schema->resultset('DiscreteCalendar')->search(
291
        {
292
            branchcode  => $branchcode,
293
            holidaytype => 'R',
294
295
        },
296
        {
297
            select  => \[ 'distinct DAY(date), MONTH(date), note'],
298
            as      => [qw/ day month note/],
299
        }
300
    );
301
302
    while (my $date = $rs->next()){
303
        push @repeatable_holidays, {
304
            day=> $date->get_column('day'),
305
            month => $date->get_column('month'),
306
            note => $date->note()
307
        };
308
    }
309
310
    return @repeatable_holidays;
311
}
312
313
sub get_week_days_holidays {
314
    my $self = shift;
315
    my $branchcode = $self->{branchcode};
316
    my @week_days;
317
    my $schema = Koha::Database->new->schema;
318
319
    my $rs = $schema->resultset('DiscreteCalendar')->search(
320
        {
321
            holidaytype => 'W',
322
            branchcode  => $branchcode,
323
        },
324
        {
325
            select      => [{ DAYOFWEEK => 'date'}, 'note'],
326
            as          => [qw/ weekday note /],
327
            distinct    => 1,
328
        }
329
    );
330
331
    while (my $date = $rs->next()){
332
        push @week_days, {
333
            weekday => ($date->get_column('weekday') -1),
334
            note    => $date->note()
335
        };
336
    }
337
338
    return @week_days;
339
}
340
=head1 edit_holiday
341
342
Modifies a date or a range of dates
343
344
C<$title> Is the title to be modified for the holiday formed by $year/$month/$day.
345
346
C<$weekday> Is the day of week for the holiday
347
348
C<$holidaytype> Is the type of the holiday :
349
    E : Exception holiday, single day.
350
    F : Floating holiday, different day each year.
351
    N : Needs validation, copied float holiday from the past
352
    R : Repeatable holiday, repeated on same date.
353
    W : Weekly holiday, same day of the week.
354
355
C<$openHour> Is the opening hour.
356
C<$closeHour> Is the closing hour.
357
C<$startDate> Is the start of the range of dates.
358
C<$endDate> Is the end of the range of dates.
359
=back
360
=cut
361
362
sub edit_holiday {
363
    my ($self, $title, $weekday, $holidaytype, $openHour, $closeHour, $startDate, $endDate, $deleteType) = @_;
364
    my $branchcode = $self->{branchcode};
365
    my $today = DateTime->today;
366
    my $schema = Koha::Database->new->schema;
367
    my $dtf = $schema->storage->datetime_parser;
368
    #String dates for Database usage
369
    my $startDate_String = $dtf->format_datetime($startDate);
370
    my $endDate_String = $dtf->format_datetime($endDate);
371
    $today = $dtf->format_datetime($today);
372
373
    my %updateValues = (
374
        isopened    => 0,
375
        note        => $title,
376
        holidaytype => $holidaytype,
377
    );
378
    $updateValues{openhour}  = $openHour if $openHour ne '';
379
    $updateValues{closehour}  = $closeHour if $closeHour ne '';
380
381
    if($holidaytype eq 'W') {
382
        #Insert/Update weekly holidays
383
        my $rs = $schema->resultset('DiscreteCalendar')->search(
384
            {
385
                branchcode  => $branchcode,
386
            },
387
            {
388
                where => \[ 'DAYOFWEEK(date) = ? and date >= ?', $weekday, $today],
389
            }
390
        );
391
392
        while (my $date = $rs->next()){
393
            $date->update(\%updateValues);
394
        }
395
    }elsif ($holidaytype eq 'E' || $holidaytype eq 'F' || $holidaytype eq 'N') {
396
        #Insert/Update Exception Float and Needs Validation holidays
397
        my $rs = $schema->resultset('DiscreteCalendar')->search(
398
            {
399
                branchcode  => $branchcode,
400
            },
401
            {
402
                where =>  \['date between DATE(?) and DATE(?) and date >= ?',$startDate_String, $endDate_String, $today]
403
            }
404
        );
405
        while (my $date = $rs->next()){
406
            $date->update(\%updateValues);
407
        }
408
409
    }elsif ($holidaytype eq 'R') {
410
        #Insert/Update repeatable holidays
411
        my $parser = DateTime::Format::Strptime->new(
412
           pattern  => '%m-%d',
413
           on_error => 'croak',
414
        );
415
        #Format the dates to have only month-day ex: 01-04 for January 4th
416
        $startDate = $parser->format_datetime($startDate);
417
        $endDate = $parser->format_datetime($endDate);
418
        my $rs = $schema->resultset('DiscreteCalendar')->search(
419
            {
420
                branchcode  => $branchcode,
421
            },
422
            {
423
                where =>  \["(DATE_FORMAT(date,'\%m-\%d') BETWEEN ? AND ? ) AND date >= ?", $startDate, $endDate, $today],
424
            }
425
        );
426
        while (my $date = $rs->next()){
427
            $date->update(\%updateValues);
428
        }
429
430
    }else {
431
        #Delete/Update date(s)
432
        my $rs = $schema->resultset('DiscreteCalendar')->search(
433
            {
434
                branchcode  => $branchcode,
435
            },
436
            {
437
                where =>  \['date between DATE(?) and DATE(?) and date >= ?',$startDate_String, $endDate_String, $today],
438
            }
439
        );
440
        #If none, the date(s) will be normal days, else,
441
        if($holidaytype eq 'none'){
442
            $updateValues{holidaytype}  ='';
443
            $updateValues{isopened}  =1;
444
        }else{
445
            delete $updateValues{holidaytype};
446
        }
447
        while (my $date = $rs->next()){
448
            if($deleteType){
449
                if($date->holidaytype() eq 'W' && $startDate_String eq $endDate_String){
450
                    $self->remove_weekly_holidays($weekday, \%updateValues, $today);
451
                }elsif($date->holidaytype() eq 'R'){
452
                    $self->remove_repeatable_holidays($startDate, $endDate, \%updateValues, $today);
453
                }
454
            }else{
455
                $date->update(\%updateValues);
456
            }
457
        }
458
    }
459
}
460
461
sub remove_weekly_holidays {
462
    my ($self, $weekday, $updateValues, $today) = @_;
463
    my $branchcode = $self->{branchcode};
464
    my $schema = Koha::Database->new->schema;
465
466
    my $rs = $schema->resultset('DiscreteCalendar')->search(
467
        {
468
            branchcode  => $branchcode,
469
            isopened    => 0,
470
            holidaytype => 'W'
471
        },
472
        {
473
            where => \["DAYOFWEEK(date) = ? and date >= ?", $weekday,$today],
474
        }
475
    );
476
477
    while (my $date = $rs->next()){
478
        $date->update($updateValues);
479
    }
480
}
481
482
sub remove_repeatable_holidays {
483
    my ($self, $startDate, $endDate, $updateValues, $today) = @_;
484
    my $branchcode = $self->{branchcode};
485
    my $schema = Koha::Database->new->schema;
486
    my $parser = DateTime::Format::Strptime->new(
487
        pattern   => '%m-%d',
488
        on_error  => 'croak',
489
    );
490
    #Format the dates to have only month-day ex: 01-04 for January 4th
491
    $startDate = $parser->format_datetime($startDate);
492
    $endDate = $parser->format_datetime($endDate);
493
494
    my $rs = $schema->resultset('DiscreteCalendar')->search(
495
        {
496
            branchcode  => $branchcode,
497
            isopened    => 0,
498
            holidaytype => 'R',
499
        },
500
        {
501
            where =>  \["(DATE_FORMAT(date,'\%m-\%d') BETWEEN ? AND ? ) AND date >= ?", $startDate, $endDate, $today],
502
        }
503
    );
504
505
    while (my $date = $rs->next()){
506
        $date->update($updateValues);
507
    }
508
}
509
510
sub copyToBranch {
511
    my ($self,$newBranch) =@_;
512
    my $branchcode = $self->{branchcode};
513
    my $schema = Koha::Database->new->schema;
514
515
    my $copyFrom = $schema->resultset('DiscreteCalendar')->search(
516
        {
517
            branchcode  => $branchcode
518
        },
519
        {
520
            columns     => [ qw/ date isopened note holidaytype openhour closehour /]
521
        }
522
    );
523
    while (my $copyDate = $copyFrom->next()){
524
        my $copyTo = $schema->resultset('DiscreteCalendar')->search(
525
            {
526
                branchcode  => $newBranch,
527
                date        => $copyDate->date(),
528
            },
529
            {
530
                columns => [ qw/ date branchcode isopened note holidaytype openhour closehour /]
531
            }
532
        );
533
        #if the date does not exist in the copyTO branch, than skip it.
534
        if($copyTo->count ==0){
535
            next;
536
        }
537
        $copyTo->next()->update({
538
            isopened    => $copyDate->isopened(),
539
            holidaytype => $copyDate->holidaytype(),
540
            note        => $copyDate->note(),
541
            openhour    => $copyDate->openhour(),
542
            closehour   => $copyDate->closehour()
543
        });
544
    }
545
}
546
547
sub isOpened {
548
    my($self, $date) = @_;
549
    my $branchcode = $self->{branchcode};
550
    my $schema = Koha::Database->new->schema;
551
    my $dtf = $schema->storage->datetime_parser;
552
    $date= $dtf->format_datetime($date);
553
    #if the date is not found
554
    my $isOpened = -1;
555
    my $rs = $schema->resultset('DiscreteCalendar')->search(
556
        {
557
            branchcode => $branchcode,
558
        },
559
        {
560
            where   => \['date = DATE(?)', $date]
561
        }
562
    );
563
    $isOpened = $rs->next()->isopened() if $rs->count() != 0;
564
565
    return $isOpened;
566
}
567
568
sub is_holiday {
569
    my($self, $date) = @_;
570
    my $branchcode = $self->{branchcode};
571
    my $schema = Koha::Database->new->schema;
572
    my $dtf = $schema->storage->datetime_parser;
573
    $date= $dtf->format_datetime($date);
574
    #if the date is not found
575
    my $isHoliday = -1;
576
    my $rs = $schema->resultset('DiscreteCalendar')->search(
577
        {
578
            branchcode => $branchcode,
579
        },
580
        {
581
            where   => \['date = DATE(?)', $date]
582
        }
583
    );
584
585
    if($rs->count() != 0){
586
        $isHoliday = 0 if $rs->first()->isopened();
587
        $isHoliday = 1 if !$rs->first()->isopened();
588
    }
589
590
    return $isHoliday;
591
}
592
593
sub copyHoliday {
594
    my ($self, $from_startDate, $from_endDate, $to_startDate, $to_endDate, $daysnumber) = @_;
595
    my $branchcode = $self->{branchcode};
596
    my $copyFromType =  $from_startDate && $from_endDate eq '' ? 'oneDay': 'range';
597
    my $schema = Koha::Database->new->schema;
598
    my $dtf = $schema->storage->datetime_parser;
599
600
    if ($copyFromType eq 'oneDay'){
601
        my $where;
602
        $to_startDate = $dtf->format_datetime($to_startDate);
603
        if ($to_startDate && $to_endDate) {
604
            $to_endDate = $dtf->format_datetime($to_endDate);
605
            $where = \["date between ? and ?", $to_startDate, $to_endDate];
606
        } else {
607
            $where = \['date = ?', $to_startDate];
608
        }
609
610
        $from_startDate = $dtf->format_datetime($from_startDate);
611
        my $fromDate = $schema->resultset('DiscreteCalendar')->search(
612
            {
613
                branchcode  => $branchcode,
614
                date        => $from_startDate
615
            }
616
        );
617
        my $toDates = $schema->resultset('DiscreteCalendar')->search(
618
            {
619
                branchcode  => $branchcode,
620
            },
621
            {
622
                where       => $where
623
            }
624
        );
625
626
        my $copyDate = $fromDate->next();
627
        while (my $date = $toDates->next()){
628
            $date->update({
629
                isopened    => $copyDate->isopened(),
630
                holidaytype => $copyDate->holidaytype(),
631
                note        => $copyDate->note(),
632
                openhour    => $copyDate->openhour(),
633
                closehour   => $copyDate->closehour()
634
            })
635
        }
636
637
    }else{
638
        my $endDate = dt_from_string($from_endDate);
639
        $to_startDate = $dtf->format_datetime($to_startDate);
640
        $to_endDate = $dtf->format_datetime($to_endDate);
641
        if($daysnumber ==6){
642
            for (my $tempDate = $from_startDate->clone(); $tempDate <= $endDate;$tempDate->add(days => 1)){
643
                my $formatedDate = $dtf->format_datetime($tempDate);
644
                my $fromDate = $schema->resultset('DiscreteCalendar')->search(
645
                    {
646
                        branchcode  => $branchcode,
647
                        date        => $formatedDate,
648
                    },
649
                    {
650
                        select  => [{ DAYOFWEEK => 'date' }],
651
                        as      => [qw/ weekday /],
652
                        columns =>[ qw/ holidaytype note openhour closehour note/]
653
                    }
654
                );
655
                my $copyDate = $fromDate->next();
656
                my $weekday = $copyDate->get_column('weekday');
657
658
                my $toDate = $schema->resultset('DiscreteCalendar')->search(
659
                    {
660
                        branchcode  => $branchcode,
661
662
                    },
663
                    {
664
                        where       => \['date between ? and ? and DAYOFWEEK(date) = ?',$to_startDate, $to_endDate, $weekday]
665
                    }
666
                );
667
                my $copyToDate = $toDate->next();
668
                $copyToDate->update({
669
                    isopened    => $copyDate->isopened(),
670
                    holidaytype => $copyDate->holidaytype(),
671
                    note        => $copyDate->note(),
672
                    openhour    => $copyDate->openhour(),
673
                    closehour   => $copyDate->closehour()
674
                });
675
676
            }
677
        }else{
678
            my $to_startDate = dt_from_string($to_startDate);
679
            my $to_endDate = dt_from_string($to_endDate);
680
            for (my $tempDate = $from_startDate->clone(); $tempDate <= $endDate;$tempDate->add(days => 1)){
681
                my $from_formatedDate = $dtf->format_datetime($tempDate);
682
                my $fromDate = $schema->resultset('DiscreteCalendar')->search(
683
                    {
684
                        branchcode  => $branchcode,
685
                        date        => $from_formatedDate,
686
                    },
687
                    {
688
                        order_by    => { -asc => 'date' }
689
                    }
690
                );
691
                my $to_formatedDate = $dtf->format_datetime($to_startDate);
692
                my $toDate = $schema->resultset('DiscreteCalendar')->search(
693
                    {
694
                        branchcode  => $branchcode,
695
                        date        => $to_formatedDate
696
                    },
697
                    {
698
                        order_by    => { -asc => 'date' }
699
                    }
700
                );
701
                my $copyDate = $fromDate->next();
702
                $toDate->next()->update({
703
                    isopened    => $copyDate->isopened(),
704
                    holidaytype => $copyDate->holidaytype(),
705
                    note        => $copyDate->note(),
706
                    openhour    => $copyDate->openhour(),
707
                    closehour   => $copyDate->closehour()
708
                });
709
                $to_startDate->add(days =>1);
710
            }
711
        }
712
713
714
    }
715
}
716
717
sub days_between {
718
    my ($self, $start_date, $end_date, ) = @_;
719
    my $branchcode = $self->{branchcode};
720
721
    if ( $start_date->compare($end_date) > 0 ) {
722
        # swap dates
723
        my $int_dt = $end_date;
724
        $end_date = $start_date;
725
        $start_date = $int_dt;
726
    }
727
728
    my $schema = Koha::Database->new->schema;
729
    my $dtf = $schema->storage->datetime_parser;
730
    $start_date = $dtf->format_datetime($start_date);
731
    $end_date = $dtf->format_datetime($end_date);
732
733
    my $days_between = $schema->resultset('DiscreteCalendar')->search(
734
        {
735
            branchcode  => $branchcode,
736
            isopened    => 1,
737
        },
738
        {
739
            where       => \['date >= date(?) and date < date(?)',$start_date, $end_date]
740
        }
741
    );
742
743
    return DateTime::Duration->new( days => $days_between->count());
744
}
745
746
sub next_open_day {
747
    my ( $self, $date ) = @_;
748
    my $branchcode = $self->{branchcode};
749
    my $schema = Koha::Database->new->schema;
750
    my $dtf = $schema->storage->datetime_parser;
751
    $date = $dtf->format_datetime($date);
752
753
    my $rs = $schema->resultset('DiscreteCalendar')->search(
754
        {
755
            branchcode  => $branchcode,
756
            isopened    => 1,
757
        },
758
        {
759
            where       => \['date > date(?)', $date],
760
            order_by    => { -asc => 'date' },
761
            rows        => 1
762
        }
763
    );
764
    return dt_from_string( $rs->next()->date(), 'iso');
765
}
766
767
sub prev_open_day {
768
    my ( $self, $date ) = @_;
769
    my $branchcode = $self->{branchcode};
770
    my $schema = Koha::Database->new->schema;
771
    my $dtf = $schema->storage->datetime_parser;
772
    $date = $dtf->format_datetime($date);
773
774
    my $rs = $schema->resultset('DiscreteCalendar')->search(
775
        {
776
            branchcode  => $branchcode,
777
            isopened    => 1,
778
        },
779
        {
780
            where       => \['date < date(?)', $date],
781
            order_by    => { -desc => 'date' },
782
            rows        => 1
783
        }
784
    );
785
    return dt_from_string( $rs->next()->date(), 'iso');
786
}
787
788
sub hours_between {
789
    my ($self, $start_dt, $end_dt) = @_;
790
    my $branchcode = $self->{branchcode};
791
    my $schema = Koha::Database->new->schema;
792
    my $dtf = $schema->storage->datetime_parser;
793
    my $start_date = $start_dt->clone();
794
    my $end_date = $end_dt->clone();
795
    my $duration = $end_date->delta_ms($start_date);
796
    $start_date->truncate( to => 'day' );
797
    $end_date->truncate( to => 'day' );
798
799
    # NB this is a kludge in that it assumes all days are 24 hours
800
    # However for hourly loans the logic should be expanded to
801
    # take into account open/close times then it would be a duration
802
    # of library open hours
803
    my $skipped_days = 0;
804
    $start_date = $dtf->format_datetime($start_date);
805
    $end_date = $dtf->format_datetime($end_date);
806
    my $hours_between = $schema->resultset('DiscreteCalendar')->search(
807
        {
808
            branchcode  =>  $branchcode,
809
            isopened    => 0
810
        },
811
        {
812
            where  => \[ 'date between ? and ?', $start_date, $end_date],
813
        }
814
    );
815
816
    if ($skipped_days = $hours_between->count()) {
817
        $duration->subtract_duration(DateTime::Duration->new( hours => 24 * $skipped_days));
818
    }
819
820
    return $duration;
821
}
822
823
sub open_hours_between {
824
    my ($self, $start_date, $end_date) = @_;
825
    my $branchcode = $self->{branchcode};
826
    my $schema = Koha::Database->new->schema;
827
    my $dtf = $schema->storage->datetime_parser;
828
    $start_date = $dtf->format_datetime($start_date);
829
    $end_date = $dtf->format_datetime($end_date);
830
831
    my $working_hours_between = $schema->resultset('DiscreteCalendar')->search(
832
        {
833
            branchcode  => $branchcode,
834
            isopened    => 1,
835
        },
836
        {
837
            select  => \['sum(time_to_sec(timediff(closehour, openhour)) / 3600)'],
838
            as      => [qw /hours_between/],
839
            where   => \['date BETWEEN DATE(?) AND DATE(?)', $start_date, $end_date]
840
        }
841
    );
842
843
    my $loan_day = $schema->resultset('DiscreteCalendar')->search(
844
        {
845
            branchcode  => $branchcode,
846
        },
847
        {
848
            where       => \['date = DATE(?)', $start_date],
849
        }
850
    );
851
852
    my $return_day = $schema->resultset('DiscreteCalendar')->search(
853
        {
854
            branchcode  => $branchcode,
855
        },
856
        {
857
            where       => \['date = DATE(?)', $end_date],
858
        }
859
    );
860
861
    #Capture the time portion of the date
862
    $start_date =~ /\s(.*)/;
863
    my $loan_date_time = $1;
864
    $end_date =~ /\s(.*)/;
865
    my $return_date_time = $1;
866
867
    my $not_used_hours = $schema->resultset('DiscreteCalendar')->search(
868
        {
869
            branchcode  => $branchcode,
870
            isopened    => 1,
871
        },
872
        {
873
            select  => \[ '(time_to_sec(timediff(?, ?)) + time_to_sec(timediff(?, ?)) ) / 3600', $return_day->next()->closehour(), $return_date_time, $loan_date_time, $loan_day->next()->openhour()],
874
            as      => [qw /not_used_hours/],
875
        }
876
    );
877
878
    return ($working_hours_between->next()->get_column('hours_between') - $not_used_hours->next()->get_column('not_used_hours'));
879
}
880
sub addDate {
881
    my ( $self, $startdate, $add_duration, $unit ) = @_;
882
883
    # Default to days duration (legacy support I guess)
884
    if ( ref $add_duration ne 'DateTime::Duration' ) {
885
        $add_duration = DateTime::Duration->new( days => $add_duration );
886
    }
887
888
    $unit ||= 'days'; # default days ?
889
    my $dt;
890
891
    if ( $unit eq 'hours' ) {
892
        # Fixed for legacy support. Should be set as a branch parameter
893
        my $return_by_hour = 10;
894
895
        $dt = $self->addHours($startdate, $add_duration, $return_by_hour);
896
    } else {
897
        # days
898
        $dt = $self->addDays($startdate, $add_duration);
899
    }
900
901
    return $dt;
902
}
903
904
sub addHours {
905
    my ( $self, $startdate, $hours_duration, $return_by_hour ) = @_;
906
    my $base_date = $startdate->clone();
907
908
    $base_date->add_duration($hours_duration);
909
910
    # If we are using the calendar behave for now as if Datedue
911
    # was the chosen option (current intended behaviour)
912
913
    if ( $self->{days_mode} ne 'Days' &&
914
    $self->is_holiday($base_date) ) {
915
916
        if ( $hours_duration->is_negative() ) {
917
            $base_date = $self->prev_open_day($base_date);
918
        } else {
919
            $base_date = $self->next_open_day($base_date);
920
        }
921
922
        $base_date->set_hour($return_by_hour);
923
924
    }
925
926
    return $base_date;
927
}
928
929
sub addDays {
930
    my ( $self, $startdate, $days_duration ) = @_;
931
    my $base_date = $startdate->clone();
932
933
    $self->{days_mode} ||= q{};
934
935
    if ( $self->{days_mode} eq 'Calendar' ) {
936
        # use the calendar to skip all days the library is closed
937
        # when adding
938
        my $days = abs $days_duration->in_units('days');
939
940
        if ( $days_duration->is_negative() ) {
941
            while ($days) {
942
                $base_date = $self->prev_open_day($base_date);
943
                --$days;
944
            }
945
        } else {
946
            while ($days) {
947
                $base_date = $self->next_open_day($base_date);
948
                --$days;
949
            }
950
        }
951
952
    } else { # Days or Datedue
953
        # use straight days, then use calendar to push
954
        # the date to the next open day if Datedue
955
        $base_date->add_duration($days_duration);
956
957
        if ( $self->{days_mode} eq 'Datedue' ) {
958
            # Datedue, then use the calendar to push
959
            # the date to the next open day if holiday
960
            if (!$self->isOpened($base_date) ) {
961
962
                if ( $days_duration->is_negative() ) {
963
                    $base_date = $self->prev_open_day($base_date);
964
                } else {
965
                    $base_date = $self->next_open_day($base_date);
966
                }
967
            }
968
        }
969
    }
970
971
    return $base_date;
972
}
973
974
1;
(-)a/installer/data/mysql/atomicupdate/bug_17015_generate_discrete_calendar.perl (+165 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 Getopt::Long;
11
use C4::Context;
12
13
# Options
14
my $help = 0;
15
my $daysInFuture = 365;
16
GetOptions (
17
            'days|?|d=i' => \$daysInFuture,
18
            'help|?|h' => \$help);
19
my $usage = << 'ENDUSAGE';
20
21
Script that manages the discrete_calendar table.
22
23
This script has the following parameters :
24
    --days --d : how many days in the future will be created, by default it's 365
25
    -h --help: this message
26
    --generate: fills discrete_calendar table with dates from the last two years and the next one
27
28
ENDUSAGE
29
30
if ($help) {
31
    print $usage;
32
    exit;
33
}
34
my $dbh = C4::Context->dbh;
35
36
my $currentDate = DateTime->today;
37
38
# two years ago
39
my $startDate = DateTime->new(
40
day       => $currentDate->day(),
41
month     => $currentDate->month(),
42
year      => $currentDate->year()-2,
43
time_zone => C4::Context->tz()
44
)->truncate( to => 'day' );
45
46
# a year into the future
47
my $endDate = DateTime->new(
48
day       => $currentDate->day(),
49
month     => $currentDate->month(),
50
year      => $currentDate->year(),
51
time_zone => C4::Context->tz()
52
)->truncate( to => 'day' );
53
$endDate->add(days => $daysInFuture);
54
55
#Added a default (standard) branch.
56
my $add_default_branch = 'INSERT INTO branches (branchname, branchcode) VALUES(?,?)';
57
my $add_Branch_Sth = $dbh->prepare($add_default_branch);
58
$add_Branch_Sth->execute('Default', 'DFLT');
59
# finds branches;
60
my $selectBranchesSt = 'SELECT branchcode FROM branches';
61
my $selectBranchesSth = $dbh->prepare($selectBranchesSt);
62
$selectBranchesSth->execute();
63
my @branches = ();
64
while ( my $branchCode = $selectBranchesSth->fetchrow_array ) {
65
66
    push @branches,$branchCode;
67
}
68
69
# finds what days are closed for each branch
70
my %repeatableHolidaysPerBranch = ();
71
my %specialHolidaysPerBranch = ();
72
my $selectWeeklySt;
73
my $selectWeeklySth;
74
75
76
foreach my $branch (@branches){
77
78
    $selectWeeklySt = 'SELECT weekday, title, day, month FROM repeatable_holidays WHERE branchcode = ?';
79
    $selectWeeklySth = $dbh->prepare($selectWeeklySt);
80
    $selectWeeklySth->execute($branch);
81
82
    my @weeklyHolidays = ();
83
84
    while ( my ($weekDay, $title, $day, $month) = $selectWeeklySth->fetchrow_array ) {
85
        push @weeklyHolidays,{weekday => $weekDay, title => $title, day => $day, month => $month};
86
87
    }
88
89
    $repeatableHolidaysPerBranch{$branch} = \@weeklyHolidays;
90
91
    my $selectSpecialHolidayDateSt = 'SELECT day,month,year,title FROM special_holidays WHERE branchcode = ? AND isexception = 0';
92
    my $specialHolidayDatesSth = $dbh->prepare($selectSpecialHolidayDateSt);
93
    $specialHolidayDatesSth -> execute($branch);
94
    # Tranforms dates from specialHolidays table in DateTime for our new table
95
    my @specialHolidayDates = ();
96
    while ( my ($day, $month, $year, $title) = $specialHolidayDatesSth->fetchrow_array ) {
97
98
        my $specialHolidayDate = DateTime->new(
99
        day       => $day,
100
        month     => $month,
101
        year      => $year,
102
        time_zone => C4::Context->tz()
103
        )->truncate( to => 'day' );
104
        push @specialHolidayDates,{date=>$specialHolidayDate, title=> $title};
105
    }
106
107
    $specialHolidaysPerBranch{$branch} = \@specialHolidayDates;
108
}
109
# Fills table with dates and sets 'isopened' according to the branch's weekly restrictions (repeatable_holidays)
110
my $insertDateSt;
111
my $insertDateSth;
112
113
# Loop that does everything in the world
114
for (my $tempDate = $startDate->clone(); $tempDate <= $endDate;$tempDate->add(days => 1)){
115
    foreach my $branch (@branches){
116
        my $dayOfWeek = $tempDate->day_of_week;
117
        # Representation fix
118
        # DateTime object dow (1-7) where Monday is 1
119
        # Arrays are 0-based where 0 = Sunday, not 7.
120
        $dayOfWeek -=1 unless $dayOfWeek ==7;
121
        $dayOfWeek =0 if $dayOfWeek ==7;
122
123
        my $openhour = "09:00:00";
124
        my $closehour = "17:00:00";
125
126
        # Finds closed days
127
        my $isOpened =1;
128
        my $specialDescription = "";
129
        my $holidaytype ='';
130
        $dayOfWeek = $tempDate->day_of_week;
131
        foreach my $holidayWeekDay (@{$repeatableHolidaysPerBranch{$branch}}){
132
            if($holidayWeekDay->{weekday} && $dayOfWeek == $holidayWeekDay->{weekday}){
133
                $isOpened = 0;
134
                $specialDescription = $holidayWeekDay->{title};
135
                $holidaytype = 'W';
136
            }elsif($holidayWeekDay->{day} && $holidayWeekDay->{month}){
137
                my $date = DateTime->new(
138
                day       => $holidayWeekDay->{day},
139
                month     => $holidayWeekDay->{month},
140
                year      => $tempDate->year(),
141
                time_zone => C4::Context->tz()
142
                )->truncate( to => 'day' );
143
144
                if ($tempDate == $date) {
145
                    $isOpened = 0;
146
                    $specialDescription = $holidayWeekDay->{title};
147
                    $holidaytype = 'R';
148
                }
149
            }
150
        }
151
152
        foreach my $specialDate (@{$specialHolidaysPerBranch{$branch}}){
153
            if($tempDate->datetime() eq $specialDate->{date}->datetime() ){
154
                $isOpened = 0;
155
                $specialDescription = $specialDate->{title};
156
                $holidaytype = 'E';
157
            }
158
        }
159
        #final insert statement
160
161
        $insertDateSt = 'INSERT INTO discrete_calendar (date,branchcode,isopened,holidaytype,note,openhour,closehour) VALUES (?,?,?,?,?,?,?)';
162
        $insertDateSth = $dbh->prepare($insertDateSt);
163
        $insertDateSth->execute($tempDate,$branch,$isOpened,$holidaytype,$specialDescription,$openhour,$closehour);
164
    }
165
}
(-)a/installer/data/mysql/atomicupdate/bug_17015_koha_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
    `isopened` tinyint(1) DEFAULT 1,
9
    `holidaytype` varchar(1) DEFAULT '',
10
    `note` varchar(30) DEFAULT '',
11
    `openhour` time NOT NULL,
12
    `closehour` time NOT NULL,
13
    PRIMARY KEY (`branchcode`,`date`)
14
);
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/discrete_calendar.tt (+662 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
            holidaytype:"[% date.holidaytype %]",
19
            openhour: "[% date.openhour %]",
20
            closehour: "[% date.closehour %]"
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
        $('#openHour').val(datesInfos[dateString].openhour);
45
        $('#closeHour').val(datesInfos[dateString].closehour);
46
47
        //This changes the label of the date type on the edit panel
48
        if(holidayType == 'W') {
49
            $("#holtype").attr("class","key repeatableweekly").html(_("Holiday repeating weekly"));
50
        } else if(holidayType == 'R') {
51
            $("#holtype").attr("class","key repeatableyearly").html(_("Holiday repeating yearly"));
52
        } else if(holidayType == 'F') {
53
            $("#holtype").attr("class","key float").html(_("Floating holiday"));
54
        } else if(holidayType == 'N') {
55
            $("#holtype").attr("class","key exception").html(_("Needs validation"));
56
        } else if(holidayType == 'E') {
57
            $("#holtype").attr("class","key holiday").html(_("Unique holiday"));
58
        } else{
59
            $("#holtype").attr("class","key normalday").html(_("Working day "));
60
        }
61
62
        //Select the correct holiday type on the dropdown menu
63
        if (datesInfos[dateString].holidaytype !=''){
64
            var type = datesInfos[dateString].holidaytype;
65
            $('#holidayType option[value="'+ type +'"]').attr('selected', true)
66
        }else{
67
            $('#holidayType option[value="none"]').attr('selected', true)
68
        }
69
70
        //If it is a weekly or repeatable holiday show the option to delete the type
71
        if(datesInfos[dateString].holidaytype == 'W' || datesInfos[dateString].holidaytype == 'R'){
72
            $('#deleteType').show("fast");
73
        }else{
74
            $('#deleteType').hide("fast");
75
        }
76
77
        //This value is to disable and hide input when the date is in the past, because you can't edit it.
78
        var value = false;
79
        var today = new Date();
80
        today.setHours(0,0,0,0);
81
        if(date_obj < today ){
82
            $("#holtype").attr("class","key past-date").html(_("Past date"));
83
            $("#CopyRadioButton").attr("checked", "checked");
84
            value = true;
85
            $(".CopyDatePanel").toggle(value);
86
        }
87
        $("#title").prop('disabled', value);
88
        $("#holidayType select").prop('disabled', value);
89
        $("#openHour").prop('disabled', value);
90
        $("#closeHour").prop('disabled', value);
91
        $("#EditRadioButton").parent().toggle(!value);
92
93
    }
94
95
    function hidePanel(aPanelName) {
96
        $("#"+aPanelName).slideUp("fast");
97
    }
98
99
    function changeBranch () {
100
        var branch = $("#branch option:selected").val();
101
        location.href='/cgi-bin/koha/tools/discrete_calendar.pl?branch=' + branch + '&calendardate=' + "[% calendardate %]";
102
    }
103
104
    function Help() {
105
        newin=window.open("/cgi-bin/koha/help.pl","KohaHelp",'width=600,height=600,toolbar=false,scrollbars=yes');
106
    }
107
108
    // This function gives css clases to each kind of day
109
    function dateStatusHandler(date) {
110
        date = getSeparetedDate(date);
111
        var day = date.day;
112
        var month = date.month;
113
        var year = date.year;
114
        var weekDay = date.weekDay;
115
        var dayName = weekdays[weekDay];
116
        var dateString = date.dateString;
117
        var today = new Date();
118
        today.setHours(0,0,0,0);
119
120
        if (datesInfos[dateString] && datesInfos[dateString].holidaytype =='W'){
121
            return [true, "repeatableweekly", _("Weekly holiday: %s").format(datesInfos[dateString].title)];
122
        } else if (datesInfos[dateString] && datesInfos[dateString].holidaytype == 'R') {
123
            return [true, "repeatableyearly", _("Yearly holiday: %s").format(datesInfos[dateString].title)];
124
        } else if (datesInfos[dateString] && datesInfos[dateString].holidaytype == 'N') {
125
            return [true, "exception", _("Need validation: %s").format(datesInfos[dateString].title)];
126
        } else if (datesInfos[dateString] && datesInfos[dateString].holidaytype == 'F') {
127
            return [true, "float", _("Floating holiday: %s").format(datesInfos[dateString].title)];
128
        } else if (datesInfos[dateString] && datesInfos[dateString].holidaytype == 'E') {
129
            return [true, "holiday", _("Single holiday: %s").format(datesInfos[dateString].title)];
130
        } else {
131
            if(date.date_obj < today ){
132
                return [true, "past-date", _("Past day")];
133
            }else{
134
                return [true, "normalday", _("Normal day")];
135
            }
136
        }
137
    }
138
139
    /* This function is in charge of showing the correct panel considering the kind of holiday */
140
    function dateChanged(text, date) {
141
        date = getSeparetedDate(date);
142
        var day = date.day;
143
        var month = date.month;
144
        var year = date.year;
145
        var weekDay = date.weekDay;
146
        var dayName = weekdays[weekDay];
147
        var dateString = date.dateString;
148
        var date_obj = date.date_obj;
149
        //set value of form hidden field
150
        $('#from_copyFrom').val(text);
151
152
        showHoliday(date_obj, dateString, dayName, day, month, year, weekDay, datesInfos[dateString].title, datesInfos[dateString].holidaytype);
153
    }
154
155
    /**
156
    * This function seperate a given date object a returns an array containing all needed information about the date.
157
    */
158
    function getSeparetedDate(date){
159
        var mydate = new Array();
160
        var day = (date.getDate() < 10 ? '0' : '') + date.getDate();
161
        var month = ((date.getMonth()+1) < 10 ? '0' : '') + (date.getMonth() +1);
162
        var year = date.getFullYear();
163
        var weekDay = date.getDay();
164
        //iso date string
165
        var dateString = year + '-' + month + '-' + day;
166
        mydate = {
167
            date_obj : date,
168
            dateString : dateString,
169
            weekDay: weekDay,
170
            year: year,
171
            month: month,
172
            day: day
173
        };
174
175
        return mydate;
176
    }
177
178
    /**
179
    * Valide the forms before send them to the backend
180
    */
181
    function validateForm(form){
182
        if(form =='newHoliday' && $('#CopyRadioButton').is(':checked')){
183
            if($('#to_copyFromDatePicker').val() =='' || $('#to_copyToDatePicker').val() ==''){
184
                alert("You have to pick a FROM and TO in the Copy to different dates.");
185
                return false;
186
            }else if ($('#from_copyToDatePicker').val()){
187
                var from_DateFrom = new Date($("#jcalendar-container").datepicker("getDate"));
188
                var from_DateTo = new Date($('#from_copyToDatePicker').datepicker("getDate"));
189
                var to_DateFrom = new Date($('#to_copyFromDatePicker').datepicker("getDate"));
190
                var to_DateTo = new Date($('#to_copyToDatePicker').datepicker("getDate"));
191
192
                var from_start = Math.round( from_DateFrom.getTime() / (3600*24*1000)); //days as integer from..
193
                var from_end   = Math.round( from_DateTo.getTime() / (3600*24*1000));
194
                var to_start = Math.round( to_DateFrom.getTime() / (3600*24*1000));
195
                var to_end   = Math.round( to_DateTo.getTime() / (3600*24*1000));
196
197
                var from_daysDiff = from_end - from_start;
198
                var to_daysDiff = to_end - to_start;
199
200
                if(from_daysDiff == to_daysDiff || from_daysDiff ==1){
201
                    $('#daysnumber').val(to_daysDiff);
202
                    return true;
203
                }else{
204
                    alert("You have to pick the same number of days if you choose 2 ranges");
205
                    return false;
206
                }
207
            }
208
        }else if(form == 'CopyCalendar'){
209
            if ($('#newBranch').val() ==''){
210
                alert("Please select a copy to calendar.");
211
                return false;
212
            }else{
213
                return true;
214
            }
215
        }else {
216
            return true;
217
        }
218
    }
219
220
    function go_to_date(isoDate){
221
        //I added the time to get around the timezone
222
        var date = getSeparetedDate(new Date(isoDate + " 00:00:00"));
223
        var day = date.day;
224
        var month = date.month;
225
        var year = date.year;
226
        var weekDay = date.weekDay;
227
        var dayName = weekdays[weekDay];
228
        var dateString = date.dateString;
229
        var date_obj = date.date_obj;
230
231
        $("#jcalendar-container").datepicker("setDate", date_obj);
232
        showHoliday(date_obj, dateString, dayName, day, month, year, weekDay, datesInfos[dateString].title, datesInfos[dateString].holidaytype);
233
    }
234
235
    /**
236
    *Check if date range have the same opening, closing hours and holiday type if there's one.
237
    */
238
    function checkRange(date){
239
        date = new Date(date);
240
        $('#toDate').val(getSeparetedDate(date).dateString);
241
        var fromDate = new Date($("#jcalendar-container").datepicker("getDate"));
242
        var sameHoliday =true;
243
        var sameOpenHours =true;
244
        var sameCloseHours =true;
245
246
        for (var i = fromDate; i <= date ; i.setDate(i.getDate() + 1)) {
247
            var myDate1 = getSeparetedDate(i);
248
            var date1 = myDate1.dateString;
249
            var holidayType1 = datesInfos[date1].holidaytype;
250
            var openhours1 = datesInfos[date1].openhour;
251
            var closehours1 = datesInfos[date1].closehour;
252
            for (var j = fromDate; j <= date ; j.setDate(j.getDate() + 1)) {
253
                var myDate2 = getSeparetedDate(j);
254
                var date2 = myDate2.dateString;
255
                var holidayType2 = datesInfos[date2].holidaytype;
256
                var openhours2 = datesInfos[date2].openhour;
257
                var closehours2 = datesInfos[date2].closehour;
258
259
                if (sameHoliday && holidayType1 != holidayType2){
260
                    $('#holidayType option[value="empty"]').attr('selected', true)
261
                    sameHoliday=false;
262
                }
263
                if(sameOpenHours && (openhours1 != openhours2)){
264
                    $('#openHour').val('');
265
                    sameOpenHours=false;
266
                }
267
                if(sameCloseHours && (closehours1 != closehours2)){
268
                    $('#closeHour').val('');
269
                    sameCloseHours=false;
270
                }
271
            }
272
            if (!sameOpenHours && !sameCloseHours && !sameHoliday){
273
                return false;
274
            }
275
        }
276
        return true;
277
    }
278
279
    $(document).ready(function() {
280
        $(".hint").hide();
281
        $("#branch").change(function(){
282
            changeBranch();
283
        });
284
        $("#holidayweeklyrepeatable>tbody>tr").each(function(){
285
            var first_td = $(this).find('td').first();
286
            first_td.html(weekdays[first_td.html()]);
287
        });
288
        $("a.helptext").click(function(){
289
            $(this).parent().find(".hint").toggle(); return false;
290
        });
291
        //Set the correct coloring, default date and the date ranges for all datepickers
292
        $.datepicker.setDefaults({
293
            beforeShowDay: function(thedate) {
294
                return dateStatusHandler(thedate);
295
            },
296
            defaultDate: new Date("[% keydate %]"),
297
            minDate: new Date("[% minDate %]"),
298
            maxDate: new Date("[% maxDate %]")
299
        });
300
        //Main datepicker
301
        $("#jcalendar-container").datepicker({
302
            onSelect: function(dateText, inst) {
303
                dateChanged(dateText, $(this).datepicker("getDate"));
304
            },
305
        });
306
        $('#from_copyToDatePicker').datepicker();
307
        $("#from_copyToDatePicker").change(function(){
308
            checkRange($(this).datepicker("getDate"));
309
            $('#from_copyTo').val(($(this).val()));
310
        });
311
        //Datepickers for copy dates feature
312
        $('#to_copyFromDatePicker').datepicker();
313
        $("#to_copyFromDatePicker").change(function(){
314
            $('#to_copyFrom').val(($(this).val()));
315
        });
316
        $('#to_copyToDatePicker').datepicker();
317
        $("#to_copyToDatePicker").change(function(){
318
            $('#to_copyTo').val(($(this).val()));
319
        });
320
        //Timepickers for open and close hours
321
        $('#openHour').timepicker({
322
            showOn : 'focus',
323
            timeFormat: 'HH:mm:ss',
324
            showSecond: false,
325
            stepMinute: 5,
326
        });
327
        $('#closeHour').timepicker({
328
            showOn : 'focus',
329
            timeFormat: 'HH:mm:ss',
330
            showSecond: false,
331
            stepMinute: 5,
332
        });
333
334
        $('.newHoliday input[type="radio"]').click(function () {
335
            if ($(this).attr("id") == "CopyRadioButton") {
336
                $(".CopyToBranchPanel").hide('fast');
337
                $(".CopyDatePanel").show('fast');
338
            } else if ($(this).attr("id") == "CopyToBranchRadioButton"){
339
                $(".CopyDatePanel").hide('fast');
340
                $(".CopyToBranchPanel").show('fast');
341
            } else{
342
                $(".CopyDatePanel").hide('fast');
343
                $(".CopyToBranchPanel").hide('fast');
344
            }
345
        });
346
        $(".hidePanel").on("click",function(){
347
            if( $(this).hasClass("showHoliday") ){
348
                hidePanel("showHoliday");
349
            }if ($(this).hasClass('newHoliday')) {
350
                hidePanel("newHoliday");
351
            }else {
352
                hidePanel("copyHoliday");
353
            }
354
        })
355
    });
356
//]]>
357
</script>
358
<!-- Datepicker colors -->
359
<style type="text/css"> .key { padding : 3px; white-space:nowrap; line-height:230%; }
360
.ui-datepicker { font-size : 150%; }
361
#jcalendar-container .ui-datepicker { font-size : 185%; }
362
.ui-datepicker th, .ui-datepicker .ui-datepicker-title select { font-size : 80%; }
363
.ui-datepicker td a { padding : .5em; }
364
.ui-datepicker td span { padding : .5em; border : 1px solid #BCBCBC;}
365
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { font-size : 80%; }
366
.key { padding : 3px; white-space:nowrap; line-height:230%; }
367
.normalday { background-color :  #EDEDED; color :  Black; border : 1px solid #BCBCBC; }
368
.ui-datepicker-unselectable { padding :.5em; white-space:nowrap;}
369
.ui-state-disabled { padding :.5em; white-space:nowrap;}
370
.exception { background-color :  #b3d4ff; color :  Black; border : 1px solid #BCBCBC; }
371
.past-date { background-color :  #e6e6e6; color :  #555555; border : 1px solid #BCBCBC; }
372
td.past-date a.ui-state-default { background : #e6e6e6 ; color :  #555555; }
373
.float { background-color :  #66ff33; color :  Black; border : 1px solid #BCBCBC; }
374
.holiday {  background-color :  #ffaeae; color :  Black;  border : 1px solid #BCBCBC; }
375
.repeatableweekly {  background-color :  #FFFF99; color :  Black;  border : 1px solid #BCBCBC; }
376
.repeatableyearly {  background-color :  #FFCC66; color :  Black;  border : 1px solid #BCBCBC; }
377
td.exception a.ui-state-default { background:  #b3d4ff none; color :  Black; border : 1px solid #BCBCBC; }
378
td.float a.ui-state-default { background:  #66ff33 none; color :  Black; border : 1px solid #BCBCBC; }
379
td.holiday a.ui-state-default {  background:  #ffaeae none; color :  Black;  border : 1px solid #BCBCBC; }
380
td.repeatableweekly a.ui-state-default {  background:  #FFFF99 none; color :  Black;  border : 1px solid #BCBCBC; }
381
td.repeatableyearly a.ui-state-default {  background:  #FFCC66 none; color :  Black;  border : 1px solid #BCBCBC; }
382
.information { 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; }
383
.panel { z-index : 1; display : none; border : 3px solid #CCC; padding : 3px; margin-top: .3em;  background-color: #FEFEFE; } fieldset.brief { border : 0; margin-top: 0; }
384
#showHoliday { margin : .5em 0; } h1 select { width: 20em; } div.yui-b fieldset.brief ol { font-size:100%; } div.yui-b fieldset.brief li, div.yui-b fieldset.brief li.radio  { padding:0.2em 0; } .help { margin:.3em 0;border:1px solid #EEE;padding:.3em .7em; font-size : 90%; } #holidayweeklyrepeatable, #holidaysyearlyrepeatable, #holidaysunique, #holidayexceptions { font-size : 90%; margin-bottom : 1em;} .calendar td, .calendar th, .calendar .button, .calendar tbody .day { padding : .7em; font-size: 110%; } .calendar { width: auto; border : 0; }
385
.copyHoliday form li{ display:table-row } .copyHoliday form li b, .copyHoliday form li input{ display:table-cell; margin-bottom: 2px; }
386
</style>
387
</head>
388
<body id="tools_holidays" class="tools">
389
[% INCLUDE 'header.inc' %]
390
[% INCLUDE 'cat-search.inc' %]
391
392
<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>
393
394
<div id="doc3" class="yui-t1">
395
396
   <div id="bd">
397
    <div id="yui-main">
398
    <div class="yui-b">
399
    <h2>[% Branches.GetName( branch ) %] calendar</h2>
400
    <div class="yui-g">
401
    <div class="yui-u first" style="width:60%">
402
        <label for="branch">Define the holidays for:</label>
403
        <form method="post" onsubmit="return validateForm('CopyCalendar')">
404
            <select id="branch" name="branch">
405
                [% PROCESS options_for_libraries libraries => Branches.all( selected => branch ) %]
406
            </select>
407
            Copy calendar to
408
            <select id='newBranch' name ='newBranch'>
409
                <option value=""></option>
410
                [% FOREACH l IN Branches.all() %]
411
                    [% UNLESS branch == l.branchcode %]
412
                    <option value="[% l.branchcode %]">[% l.branchname %]</option>
413
                    [% END %]
414
                [% END %]
415
            </select>
416
            <input type="hidden" name="action" value="copyBranch" />
417
            <input type="submit" value="Clone">
418
        </form>
419
            <h3>Calendar information</h3>
420
            <div id="jcalendar-container" style="float: left"></div>
421
    <!-- ***************************** Panel to deal with new holidays **********************  -->
422
    [% UNLESS  datesInfos %]
423
    <div class="alert alert-danger" style="float: left; margin-left:15px">
424
        <strong>Error!</strong> You have to run generate_discrete_calendar.pl in order to use Discrete Calendar.
425
    </div>
426
    [% END %]
427
428
    [% IF  no_branch_selected %]
429
    <div class="alert alert-danger" style="float: left; margin-left:15px">
430
        <strong>No library set!</strong> You are using the default calendar.
431
    </div>
432
    [% END %]
433
    <div class="panel newHoliday" id="newHoliday" style="float: left; margin-left:15px">
434
        <form method="post" onsubmit="return validateForm('newHoliday')">
435
            <fieldset class="brief">
436
                <h3>Edit date details</h3>
437
                <span id="holtype"></span>
438
                <ol>
439
                    <li>
440
                        <strong>Library:</strong>
441
                        <span id="newBranchNameOutput"></span>
442
                        <input type="hidden" id="branch" name="branch" />
443
                    </li>
444
                    <li>
445
                        <strong>From date:</strong>
446
                        <span id="newDaynameOutput"></span>,
447
448
                        [% 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 %]
449
450
                        <input type="hidden" id="newDayname" name="showDayname" />
451
                        <input type="hidden" id="Weekday" name="Weekday" />
452
                        <input type="hidden" id="Day" name="Day" />
453
                        <input type="hidden" id="Month" name="Month" />
454
                        <input type="hidden" id="Year" name="Year" />
455
                    </li>
456
                    <li class="dateinsert">
457
                        <b>To date: </b>
458
                        <input type="text" id="from_copyToDatePicker" name="toDate" size="20" class="datepicker" />
459
                    </li>
460
                    <li>
461
                        <label for="title">Title: </label><input type="text" name="Title" id="title" size="35" />
462
                    </li>
463
                    <li id="holidayType">
464
                        <label for="holidayType">Date type</label>
465
                        <select name ='holidayType'>
466
                            <option value="empty"></option>
467
                            <option value="none">Working day</option>
468
                            <option value="E">Unique holiday</option>
469
                            <option value="W">Weekly holiday</option>
470
                            <option value="R">Repeatable holiday</option>
471
                            <option value="F">Floating holiday</option>
472
                            <option value="N" disabled>Need validation</option>
473
                        </select>
474
                    </li>
475
                    <li class="radio" id="deleteType" style="display : none;" >
476
                        <input type="checkbox" name="deleteType" id="deleteType_checkbox" value="1" ><label for="deleteType_checkbox"> Delete this type</label>
477
                        <a href="#" class="helptext">[?]</a>
478
                        <div class="hint">Remove all repeated or weekly holidays of the selected date or week day <br> if working day is selected.</div>
479
                    </li>
480
                    <li>
481
                        <label for="openHour">Open hours: </label><input type="text" name="openHour" id='openHour' style="display :flex"  >
482
                    </li>
483
                    <li>
484
                        <label for="closeHour">Close hours: </label><input type="text" name="closeHour" id='closeHour' style="display :flex" >
485
                    </li>
486
                    <li class="radio">
487
                        <input type="radio" name="action" id="EditRadioButton" value="edit" checked/>
488
                        <label for="EditRadioButton">Edit selected dates</label>
489
                    </li>
490
                    <li class="radio">
491
                        <input type="radio" name="action" id="CopyRadioButton" value="copyDates" />
492
                        <label for="CopyRadioButton">Copy to different dates</label>
493
                        <div class="CopyDatePanel" style="display:none; padding-left:15px">
494
495
                            <b>From : </b>
496
                            <input type="text" id="to_copyFromDatePicker" size="20" class="datepicker"/>
497
                            <b>To : </b>
498
                            <input type="text" id="to_copyToDatePicker" size="20" class="datepicker"/>
499
                        </div>
500
                        <input type="hidden" name="daysnumber" id='daysnumber'>
501
                        <!-- These  yyyy-mm-dd -->
502
                        <input type="hidden" name="from_copyFrom" id='from_copyFrom'>
503
                        <input type="hidden" name="from_copyTo" id='from_copyTo'>
504
                        <input type="hidden" name="to_copyFrom" id='to_copyFrom'>
505
                        <input type="hidden" name="to_copyTo" id='to_copyTo'>
506
                    </li>
507
                </ol>
508
                <fieldset class="action">
509
                    <input type="submit" name="submit" value="Save" />
510
                    <a href="#" class="cancel hidePanel newHoliday">Cancel</a>
511
                </fieldset>
512
            </fieldset>
513
        </form>
514
    </div>
515
516
<!-- ************************************************************************************** -->
517
<!-- ******                              MAIN SCREEN CODE                            ****** -->
518
<!-- ************************************************************************************** -->
519
520
</div>
521
<div class="yui-u" style="width : 40%">
522
    <div class="help">
523
        <h4>Hints</h4>
524
        <ul>
525
            <li>Search in the calendar the day you want to set as holiday.</li>
526
            <li>Click the date to add or edit a holiday.</li>
527
            <li>Specify how the holiday should repeat.</li>
528
            <li>Click Save to finish.</li>
529
            <li>PS:
530
                <ul>
531
                    <li>You can't edit passed dates</li>
532
                    <li>Weekly holidays change open/close hours for all the days affected unless inputs are empty</li>
533
                </ul>
534
            </li>
535
        </ul>
536
        <h4>Key</h4>
537
        <p>
538
            <span class="key normalday">Working day </span>
539
            <span class="key holiday">Unique holiday</span>
540
            <span class="key repeatableweekly">Holiday repeating weekly</span>
541
            <span class="key repeatableyearly">Holiday repeating yearly</span>
542
            <span class="key float">Floating holiday</span>
543
            <span class="key exception">Need validation</span>
544
        </p>
545
    </div>
546
<div id="holiday-list">
547
548
    [% IF ( NEED_VALIDATION_HOLIDAYS ) %]
549
    <h3>Need validation holidays</h3>
550
    <table id="holidaysunique">
551
        <thead>
552
            <tr>
553
                <th class="exception">Date</th>
554
                <th class="exception">Title</th>
555
            </tr>
556
        </thead>
557
        <tbody>
558
            [% FOREACH need_validation_holiday IN NEED_VALIDATION_HOLIDAYS %]
559
            <tr>
560
                <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>
561
                <td>[% need_validation_holiday.note %]</td>
562
            </tr>
563
            [% END %]
564
        </tbody>
565
    </table>
566
    [% END %]
567
568
    [% IF ( WEEKLY_HOLIDAYS ) %]
569
    <h3>Weekly - Repeatable holidays</h3>
570
    <table id="holidayweeklyrepeatable">
571
        <thead>
572
            <tr>
573
                <th class="repeatableweekly">Day of week</th>
574
                <th class="repeatableweekly">Title</th>
575
            </tr>
576
        </thead>
577
        <tbody>
578
            [% FOREACH WEEK_DAYS_LOO IN WEEKLY_HOLIDAYS %]
579
            <tr>
580
                <td>[% WEEK_DAYS_LOO.weekday %]</td>
581
            </td>
582
            <td>[% WEEK_DAYS_LOO.note %]</td>
583
        </tr>
584
        [% END %]
585
    </tbody>
586
</table>
587
[% END %]
588
589
[% IF ( REPEATABLE_HOLIDAYS ) %]
590
<h3>Yearly - Repeatable holidays</h3>
591
<table id="holidaysyearlyrepeatable">
592
    <thead>
593
        <tr>
594
            [% IF ( dateformat == "metric" ) %]
595
            <th class="repeatableyearly">Day/month</th>
596
            [% ELSE %]
597
            <th class="repeatableyearly">Month/day</th>
598
            [% END %]
599
            <th class="repeatableyearly">Title</th>
600
        </tr>
601
    </thead>
602
    <tbody>
603
        [% FOREACH DAY_MONTH_HOLIDAYS_LOO IN REPEATABLE_HOLIDAYS %]
604
        <tr>
605
            <td><span title="[% DAY_MONTH_HOLIDAYS_LOO.DATE_SORT %]">[%DAY_MONTH_HOLIDAYS_LOO.month%]/[% DAY_MONTH_HOLIDAYS_LOO.day %]</span></td>
606
            <td>[% DAY_MONTH_HOLIDAYS_LOO.note %]</td>
607
        </tr>
608
        [% END %]
609
    </tbody>
610
</table>
611
[% END %]
612
613
[% IF ( UNIQUE_HOLIDAYS ) %]
614
<h3>Unique holidays</h3>
615
<table id="holidaysunique">
616
    <thead>
617
        <tr>
618
            <th class="holiday">Date</th>
619
            <th class="holiday">Title</th>
620
        </tr>
621
    </thead>
622
    <tbody>
623
        [% FOREACH HOLIDAYS_LOO IN UNIQUE_HOLIDAYS %]
624
        <tr>
625
            <td><a href="#doc3" onclick="go_to_date('[% HOLIDAYS_LOO.date %]')"><span title="[% HOLIDAYS_LOO.DATE_SORT %]">[% HOLIDAYS_LOO.outputdate %]</span></a></td>
626
            <td>[% HOLIDAYS_LOO.note %]</td>
627
        </tr>
628
        [% END %]
629
    </tbody>
630
</table>
631
[% END %]
632
633
[% IF ( FLOAT_HOLIDAYS ) %]
634
<h3>Floating holidays</h3>
635
<table id="holidaysunique">
636
    <thead>
637
        <tr>
638
            <th class="float">Date</th>
639
            <th class="float">Title</th>
640
        </tr>
641
    </thead>
642
    <tbody>
643
        [% FOREACH float_holiday IN FLOAT_HOLIDAYS %]
644
        <tr>
645
            <td><a href="#doc3" onclick="go_to_date('[% float_holiday.date %]')"><span title="[% float_holiday.DATE_SORT %]">[% float_holiday.outputdate %]</span></a></td>
646
            <td>[% float_holiday.note %]</td>
647
        </tr>
648
        [% END %]
649
    </tbody>
650
</table>
651
[% END %]
652
</div>
653
</div>
654
</div>
655
</div>
656
</div>
657
658
<div class="yui-b noprint">
659
[% INCLUDE 'tools-menu.inc' %]
660
</div>
661
</div>
662
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/misc/cronjobs/add_days_discrete_calendar.pl (+125 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 Getopt::Long;
11
use C4::Context;
12
13
# Options
14
my $help = 0;
15
my $daysInFuture = 1;
16
my $debug = 0;
17
GetOptions (
18
    'help|?|h'  => \$help,
19
    'n=i'       => \$daysInFuture,
20
    'd|?|debug' => \$debug);
21
22
my $usage = << 'ENDUSAGE';
23
24
This script adds days into discrete_calendar table based on the same day from the week before.
25
26
Examples :
27
    The latest date on discrete_calendar is : 28-07-2017
28
    The current date : 01-08-2016
29
    The dates that will be added are : 29-07-2017, 30-07-2017, 31-07-2017, 01-08-2017
30
Open close exemples :
31
    Date added is : 29-07-2017
32
    Opening/closing hours will be base on : 22-07-2017 (- 7 days)
33
    Library open or closed will be based on : 29-07-2017 (- 1 year)
34
This script has the following parameters:
35
    -h --help: this message
36
    -n : number of days to add in the futre, default : 1
37
    -d --debug: displays all added days and errors if there is any
38
39
ENDUSAGE
40
41
my $dbh = C4::Context->dbh;
42
my $query = "SELECT distinct weekday(date), note FROM discrete_calendar where holidaytype='W'";
43
my $stmt = $dbh->prepare($query);
44
$stmt->execute();
45
my @week_days_discrete;
46
while (my ($weekday,$note) = $stmt->fetchrow_array){
47
    push @week_days_discrete, {weekday => $weekday, note => $note};
48
}
49
50
if ($help) {
51
    print $usage;
52
    exit;
53
}
54
55
#getting the all the branches
56
my $selectBranchesSt = 'SELECT branchcode FROM discrete_calendar GROUP BY branchcode';
57
my $selectBranchesSth = $dbh->prepare($selectBranchesSt);
58
$selectBranchesSth->execute();
59
my @branches = ();
60
while ( my $branchCode = $selectBranchesSth->fetchrow_array ) {
61
62
    push @branches,$branchCode;
63
}
64
65
#get the latest date in the table
66
$query = "SELECT MAX(date) FROM discrete_calendar";
67
$stmt = $dbh->prepare($query);
68
$stmt->execute();
69
my $latestedDate = $stmt->fetchrow_array;
70
my $parser = DateTime::Format::Strptime->new(
71
    pattern => '%Y-%m-%d %H:%M:%S',
72
    on_error => 'croak',
73
);
74
$latestedDate = $parser->parse_datetime($latestedDate);
75
76
my $newDay = $latestedDate->clone();
77
$latestedDate->add(days => $daysInFuture);
78
79
for ($newDay->add(days => 1);$newDay <= $latestedDate;$newDay->add(days => 1)){
80
    my $lastWeekDay = $newDay->clone();
81
    $lastWeekDay->add(days=> -8);
82
    my $dayOfWeek = $lastWeekDay->day_of_week;
83
    # Representation fix
84
    # DateTime object dow (1-7) where Monday is 1
85
    # Arrays are 0-based where 0 = Sunday, not 7.
86
    $dayOfWeek -= 1 unless $dayOfWeek == 7;
87
    $dayOfWeek = 0 if $dayOfWeek == 7;
88
89
    #checking if it was open on the same day from last year
90
    my $yearAgo = $newDay->clone();
91
    $yearAgo = $yearAgo->add(years => -1);
92
    my $last_year = 'SELECT isopened, holidaytype, note FROM discrete_calendar WHERE date=? AND branchcode=?';
93
    my $day_last_week = "SELECT openhour, closehour FROM discrete_calendar WHERE DAYOFWEEk(date)=DAYOFWEEK(?) and date < ? order by date desc limit 1";
94
    my $add_Day = 'INSERT INTO discrete_calendar (date,branchcode,isopened,openhour,closehour) VALUES (?,?,?,?,?)';
95
    my $note ='';
96
    #insert into discrete_calendar for each branch
97
    foreach my $branchCode(@branches){
98
        $stmt = $dbh->prepare($last_year);
99
        $stmt->execute($yearAgo,$branchCode);
100
        my ($isOpened, $holidaytype, $note) = $stmt->fetchrow_array;
101
        #weekly and unique holidays are not replicated in the future
102
        if ($holidaytype && $holidaytype ne "R"){
103
            $isOpened = 1;
104
            if ($holidaytype eq "W" || $holidaytype eq "E"){
105
                $holidaytype='';
106
                $note='';
107
            }elsif ($holidaytype eq "F"){
108
                $holidaytype = 'N';
109
            }
110
        }
111
        $holidaytype = '' if $isOpened;
112
        $stmt = $dbh->prepare($day_last_week);
113
        $stmt->execute($newDay, $newDay);
114
        my ($openhour,$closehour ) = $stmt->fetchrow_array;
115
        my $add_Day = 'INSERT INTO discrete_calendar (date,branchcode,isopened,holidaytype, note,openhour,closehour) VALUES (?,?,?,?,?,?,?)';
116
        $stmt = $dbh->prepare($add_Day);
117
        $stmt->execute($newDay,$branchCode,$isOpened,$holidaytype,$note, $openhour,$closehour);
118
119
        if($debug && !$@){
120
            warn "Added day $newDay to $branchCode is opened : $isOpened, holidaytype : $holidaytype, note: $note, openhour : $openhour, closehour : $closehour \n";
121
        }elsif($@){
122
            warn "Failed to add day $newDay to $branchCode : $_\n";
123
        }
124
    }
125
}
(-)a/tools/discrete_calendar.pl (-1 / +159 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 he is using the default calendar because he does not have a library set
45
my $no_branch_selected = $calendar->{no_branch_selected};
46
47
my $weekday = $input->param('Weekday');
48
49
my $holidaytype = $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
    copyToBranch();
63
} elsif($action eq 'copyDates'){
64
    copyDates();
65
} elsif($action eq 'edit'){
66
    edit_holiday();
67
}
68
69
sub copyToBranch {
70
    $calendar->copyToBranch(scalar $input->param('newBranch'));
71
}
72
sub copyDates {
73
    my $from_startDate = $input->param('from_copyFrom') ||'';
74
    my $from_endDate = $input->param('toDate') || '';
75
    my $to_startDate = $input->param('to_copyFrom') || '';
76
    my $to_endDate = $input->param('to_copyTo') || '';
77
    my $daysnumber= $input->param('daysnumber');
78
79
    $from_startDate = dt_from_string(scalar $from_startDate) if$from_startDate  ne '';
80
    $to_startDate = dt_from_string(scalar $to_startDate) if $to_startDate ne '';
81
    $to_startDate = dt_from_string(scalar $to_startDate) if $to_startDate ne '';
82
    $to_endDate = dt_from_string(scalar $to_endDate) if $to_endDate ne '';
83
84
    $calendar->copyHoliday($from_startDate, $from_endDate, $to_startDate, $to_endDate, $daysnumber);
85
}
86
87
sub edit_holiday {
88
    my $openHour = $input->param('openHour');
89
    my $closeHour = $input->param('closeHour');
90
    my $toDate = $input->param('toDate');
91
    my $deleteType = $input->param('deleteType') || 0;
92
93
    my $startDate = dt_from_string(scalar $input->param('from_copyFrom'));
94
95
    if($toDate ne '' ) {
96
        $toDate = dt_from_string(scalar $toDate);
97
    } else{
98
        $toDate = $startDate->clone();
99
    }
100
    #MYSQL DAYOFWEEK Returns the weekday index for date (1 = Sunday, 2 = Monday, …, 7 = Saturday).
101
    #JavaScript getDay() Returns the day of the week (from 0 to 6) for the specified date. Sunday is 0, Monday is 1, and so on.
102
    $weekday+=1;
103
    $calendar->edit_holiday($title, $weekday, $holidaytype, $openHour, $closeHour, $startDate, $toDate, $deleteType);
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
my $onlymine =
112
  (      C4::Context->preference('IndependentBranches')
113
      && C4::Context->userenv
114
      && !C4::Context->IsSuperLibrarian()
115
      && C4::Context->userenv->{branch} ? 1 : 0 );
116
if ( $onlymine ) {
117
    $branch = C4::Context->userenv->{'branch'};
118
}
119
120
# Get all the holidays
121
122
#discrete_calendar weekdays
123
my @week_days = $calendar->get_week_days_holidays();
124
125
#discrete_calendar repeatable
126
my @repeatable_holidays = $calendar->get_repeatable_holidays();
127
128
#single hildays (exceptions)
129
my @unique_holidays =$calendar->get_unique_holidays();
130
#floating holidays
131
my @float_holidays =$calendar->get_float_holidays();
132
#need validation holidays
133
my @need_validation_holidays =$calendar->get_need_valdation_holidays();
134
135
#Calendar maximum date
136
my $minDate = $calendar->getMinDate($branch);
137
138
#Calendar minimum date
139
my $maxDate = $calendar->getMaxDate($branch);
140
141
my @datesInfos = $calendar->getDatesInfo($branch);
142
143
$template->param(
144
    UNIQUE_HOLIDAYS          => \@unique_holidays,
145
    FLOAT_HOLIDAYS           => \@float_holidays,
146
    NEED_VALIDATION_HOLIDAYS => \@need_validation_holidays,
147
    REPEATABLE_HOLIDAYS      => \@repeatable_holidays,
148
    WEEKLY_HOLIDAYS          => \@week_days,
149
    calendardate             => $calendardate,
150
    keydate                  => $keydate,
151
    branch                   => $branch,
152
    minDate                  => $minDate,
153
    maxDate                  => $maxDate,
154
    datesInfos               => \@datesInfos,
155
    no_branch_selected       => $no_branch_selected,
156
);
157
158
# Shows the template with the real values replaced
159
output_html_with_http_headers $input, $cookie, $template->output;

Return to bug 17015