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

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

Return to bug 17015