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

(-)a/misc/cronjobs/overdue_notices.pl (-367 / +417 lines)
Lines 298-311 alert them of items that have just become due. Link Here
298
298
299
# These variables are set by command line options.
299
# These variables are set by command line options.
300
# They are initially set to default values.
300
# They are initially set to default values.
301
my $dbh        = C4::Context->dbh();
301
my $dbh         = C4::Context->dbh();
302
my $help       = 0;
302
my $help        = 0;
303
my $man        = 0;
303
my $man         = 0;
304
my $verbose    = 0;
304
my $verbose     = 0;
305
my $nomail     = 0;
305
my $nomail      = 0;
306
my $MAX        = 90;
306
my $MAX         = 90;
307
my $test_mode  = 0;
307
my $test_mode   = 0;
308
my $frombranch = 'item-issuebranch';
308
my $frombranch  = 'item-issuebranch';
309
my $itype_level = C4::Context->preference('item-level_itypes') ? 'item' : 'biblioitem';
309
my @branchcodes;      # Branch(es) passed as parameter
310
my @branchcodes;      # Branch(es) passed as parameter
310
my @emails_to_use;    # Emails to use for messaging
311
my @emails_to_use;    # Emails to use for messaging
311
my @emails;           # Emails given in command-line parameters
312
my @emails;           # Emails given in command-line parameters
Lines 443-448 if ( defined $csvfilename ) { Link Here
443
}
444
}
444
445
445
@branches = @overduebranches unless @branches;
446
@branches = @overduebranches unless @branches;
447
448
# Setup output file if requested
446
our $fh;
449
our $fh;
447
if ( defined $htmlfilename ) {
450
if ( defined $htmlfilename ) {
448
    if ( $htmlfilename eq '' ) {
451
    if ( $htmlfilename eq '' ) {
Lines 462-471 if ( defined $htmlfilename ) { Link Here
462
    }
465
    }
463
}
466
}
464
467
468
# Setup category list
469
my @categories;
470
if (@myborcat) {
471
    @categories = @myborcat;
472
} elsif (@myborcatout) {
473
    @categories = Koha::Patron::Categories->search( { catagorycode => { 'not_in' => \@myborcatout } } )
474
        ->get_column('categorycode');
475
} else {
476
    @categories = Koha::Patron::Categories->search()->get_column('categorycode');
477
}
478
479
# Setup itemtype list
480
my @itemtypes;
481
if (@myitemtypes) {
482
    @itemtypes = @myitemtypes;
483
} elsif (@myitemtypesout) {
484
    @itemtypes =
485
        Koha::ItemTypes->search( { itemtype => { 'not_in' => \@myitemtypesout } } )->get_column('itemtype');
486
} else {
487
    @itemtypes = Koha::ItemTypes->search()->get_column('itemtype');
488
}
489
465
my %already_queued;
490
my %already_queued;
466
my %seen = map { $_ => 1 } @branches;
491
my %seen = map { $_ => 1 } @branches;
467
492
468
# Work through branches
493
# Work through branches
494
my @output_chunks;
469
foreach my $branchcode (@branches) {
495
foreach my $branchcode (@branches) {
470
    my $calendar;
496
    my $calendar;
471
    if ( C4::Context->preference('OverdueNoticeCalendar') ) {
497
    if ( C4::Context->preference('OverdueNoticeCalendar') ) {
Lines 479-534 foreach my $branchcode (@branches) { Link Here
479
    my $admin_email_address  = $library->from_email_address;
505
    my $admin_email_address  = $library->from_email_address;
480
    my $branch_email_address = C4::Context->preference('AddressForFailedOverdueNotices')
506
    my $branch_email_address = C4::Context->preference('AddressForFailedOverdueNotices')
481
        || $library->inbound_email_address;
507
        || $library->inbound_email_address;
482
    my @output_chunks;    # may be sent to mail or stdout or csv file.
508
    @output_chunks = ();    # may be sent to mail or stdout or csv file.
483
509
484
    $verbose and print "======================================\n";
510
    $verbose and print "======================================\n";
485
    $verbose and warn sprintf "branchcode : '%s' using %s\n", $branchcode, $branch_email_address;
511
    $verbose and warn sprintf "branchcode : '%s' using %s\n", $branchcode, $branch_email_address;
486
512
487
    my $sql2 = <<"END_SQL";
513
    # Work through patron categories
488
SELECT biblio.*, items.*, issues.*, biblioitems.itemtype, branchname
514
    for my $category (@categories) {
489
  FROM issues,items,biblio, biblioitems, branches b
515
490
  WHERE items.itemnumber=issues.itemnumber
516
        # Fetch all overdues for patron categories that want overdues and where the item is not lost and group by borrower
491
    AND biblio.biblionumber   = items.biblionumber
517
        my $borrower_sql = <<"END_SQL";
492
    AND b.branchcode = items.homebranch
518
SELECT
493
    AND biblio.biblionumber   = biblioitems.biblionumber
519
    issues.borrowernumber,
494
    AND issues.borrowernumber = ?
520
    borrowers.firstname,
495
    AND items.itemlost = 0
521
    borrowers.surname,
522
    borrowers.address,
523
    borrowers.address2,
524
    borrowers.city,
525
    borrowers.zipcode,
526
    borrowers.country,
527
    borrowers.email,
528
    borrowers.emailpro,
529
    borrowers.B_email,
530
    borrowers.smsalertnumber,
531
    borrowers.phone,
532
    borrowers.cardnumber,
533
    biblio.*,
534
    biblioitems.itemtype AS b_itemtype,
535
    items.*,
536
    issues.*,
537
    branches.branchname
538
FROM
539
    issues
540
JOIN
541
    borrowers ON issues.borrowernumber = borrowers.borrowernumber
542
JOIN
543
    categories ON borrowers.categorycode = categories.categorycode
544
JOIN
545
    items ON issues.itemnumber = items.itemnumber
546
JOIN
547
    biblio ON biblio.biblionumber = items.biblionumber
548
JOIN
549
    biblioitems ON biblio.biblionumber = biblioitems.biblionumber
550
JOIN
551
    branches ON branches.branchcode = items.homebranch
552
WHERE
553
    items.itemlost = 0
496
    AND TO_DAYS($date)-TO_DAYS(issues.date_due) >= 0
554
    AND TO_DAYS($date)-TO_DAYS(issues.date_due) >= 0
497
END_SQL
555
END_SQL
498
556
499
    if ($owning_library) {
557
        my @borrower_parameters;
500
        $sql2 .= ' AND items.homebranch = ? ' unless ($patron_homelibrary);
558
        if ($branchcode) {
501
    } else {
559
            if ($owning_library) {
502
        $sql2 .= ' AND issues.branchcode = ? ' unless ($patron_homelibrary);
560
                $borrower_sql .= ' AND items.homebranch=? ';
503
    }
561
            } else {
504
    my $sth2 = $dbh->prepare($sql2);
562
                $borrower_sql .= ' AND issues.branchcode=? ';
505
563
            }
506
    my @categories;
564
            push @borrower_parameters, $branchcode;
507
    if (@myborcat) {
565
        }
508
        @categories = @myborcat;
566
        if ($category) {
509
    } elsif (@myborcatout) {
567
            $borrower_sql .= ' AND borrowers.categorycode=? ';
510
        @categories = Koha::Patron::Categories->search( { catagorycode => { 'not_in' => \@myborcatout } } )
568
            push @borrower_parameters, $category;
511
            ->get_column('categorycode');
569
        }
512
    } else {
570
        if (@itemtypes) {
513
        @categories = Koha::Patron::Categories->search()->get_column('categorycode');
571
            my $placeholders = join( ", ", ("?") x @itemtypes );
514
    }
572
            if ( $itype_level eq 'item' ) {
573
                $borrower_sql .= " AND items.itype IN ($placeholders) ";
574
            } else {
575
                $borrower_sql .= " AND biblioitems.itemtype IN ($placeholders) ";
576
            }
577
            push @borrower_parameters, @itemtypes;
578
        }
579
        $borrower_sql .= '  AND categories.overduenoticerequired=1 ORDER BY issues.borrowernumber';
580
581
        # $sth gets borrower info if at least one overdue item has triggered the overdue action.
582
        my $sth = $dbh->prepare($borrower_sql);
583
        $sth->execute(@borrower_parameters);
584
585
        if ( $verbose > 1 ) {
586
            warn sprintf "--------Borrower SQL------\n";
587
            warn $borrower_sql
588
                . "\n $branchcode | "
589
                . "'$category' | "
590
                . join( "','", @itemtypes ) . " | "
591
                . $date_to_run->datetime() . ")\n";
592
            warn sprintf "--------------------------\n";
593
        }
594
        $verbose and warn sprintf "Found %s overdues for $category on $date_to_run\n", $sth->rows;
515
595
516
    my @itemtypes;
596
        my $borrowernumber;
517
    if (@myitemtypes) {
597
        my $borrower_overdues = {};
518
        @itemtypes = @myitemtypes;
519
    } elsif (@myitemtypesout) {
520
        @itemtypes =
521
            Koha::ItemTypes->search( { itemtype => { 'not_in' => \@myitemtypesout } } )->get_column('itemtype');
522
    } else {
523
        @itemtypes = Koha::ItemTypes->search()->get_column('itemtype');
524
    }
525
598
526
    # Work through category and itemtype combinations to catch overdue rules for each
599
        # Iterate over all overdues
527
    for my $category (@categories) {
600
    OVERDUE: while ( my $data = $sth->fetchrow_hashref ) {
528
        for my $itemtype (@itemtypes) {
601
            my $itemtype = $data->{itype} // $data->{b_itemtype};
529
            my $i = 0;
602
603
            # Collect triggers or act on them if we're switching borrower
604
            if ( !defined $borrower_overdues->{borrowernumber} ) {
605
                if ($verbose) {
606
                    warn "\n-----------------------------------------\n";
607
                    warn "Collecting overdue triggers for borrower " . $data->{borrowernumber} . "\n";
608
                }
609
                $borrower_overdues = { borrowernumber => $data->{borrowernumber}, branchcode => $branchcode };
610
            } elsif ( $borrower_overdues->{borrowernumber} ne $data->{borrowernumber} ) {
611
                $verbose and warn "Collected overdue triggers for " . $borrower_overdues->{borrowernumber} . "\n";
612
                _enact_trigger($borrower_overdues);
613
                if ($verbose) {
614
                    warn "\n-----------------------------------------\n";
615
                    warn "Collecting overdue triggers for borrower " . $data->{borrowernumber} . "\n";
616
                }
617
                $borrower_overdues = { borrowernumber => $data->{borrowernumber}, branchcode => $branchcode };
618
            }
530
619
531
            # Work through triggers until we run out of rules
620
            $verbose
621
                and warn "\nProcessing overdue "
622
                . $data->{issue_id}
623
                . " with branch = '$branchcode', categorycode = '$category' and itemtype = '$itemtype'\n";
624
625
            # Work through triggers until we run out of rules or find a match
626
            my $i = 0;
532
        PERIOD: while (1) {
627
        PERIOD: while (1) {
533
                $i++;
628
                $i++;
534
                my $ii            = $i + 1;
629
                my $ii            = $i + 1;
Lines 543-549 END_SQL Link Here
543
                        itemtype     => $itemtype,
638
                        itemtype     => $itemtype,
544
                    }
639
                    }
545
                );
640
                );
546
                $verbose and warn "branch '$branchcode', categorycode = $category, itemtype = $itemtype, pass $i\n";
547
641
548
                if ( !defined( $overdue_rules->{ "overdue_$i" . '_delay' } ) ) {
642
                if ( !defined( $overdue_rules->{ "overdue_$i" . '_delay' } ) ) {
549
                    last PERIOD;
643
                    last PERIOD;
Lines 558-572 END_SQL Link Here
558
                    : ($MAX)
652
                    : ($MAX)
559
                );       # issues being more than maxdays late are managed somewhere else. (borrower probably suspended)
653
                );       # issues being more than maxdays late are managed somewhere else. (borrower probably suspended)
560
654
561
                next unless defined $mindays;
562
563
                if ( !$overdue_rules->{ "overdue_$i" . '_notice' } ) {
655
                if ( !$overdue_rules->{ "overdue_$i" . '_notice' } ) {
564
                    $verbose and warn sprintf "No letter code found for pass %s\n", $i;
656
                    $verbose and warn sprintf "Trigger %s skipped, No letter code found\n", $i;
565
                    next PERIOD;
657
                    next PERIOD;
566
                }
658
                }
567
                $verbose
568
                    and warn sprintf "Using letter code '%s' for pass %s\n",
569
                    $overdue_rules->{ "overdue_$i" . '_notice' }, $i;
570
659
571
                # $letter->{'content'} is the text of the mail that is sent.
660
                # $letter->{'content'} is the text of the mail that is sent.
572
                # this text contains fields that are replaced by their value. Those fields must be written between brackets
661
                # this text contains fields that are replaced by their value. Those fields must be written between brackets
Lines 574-913 END_SQL Link Here
574
                # itemcount is interpreted here as the number of items in the overdue range defined by the current notice or all overdues < max if(-list-all).
663
                # itemcount is interpreted here as the number of items in the overdue range defined by the current notice or all overdues < max if(-list-all).
575
                # <date> <itemcount> <firstname> <lastname> <address1> <address2> <address3> <city> <postcode> <country>
664
                # <date> <itemcount> <firstname> <lastname> <address1> <address2> <address3> <city> <postcode> <country>
576
665
577
                # Fetch all overdue issues where the item is not lost and the borrower category wants overdue notices grouping by borrower
666
                # Check the overdue period matches
578
                my $borrower_sql = <<"END_SQL";
667
                my $days_between;
579
SELECT issues.borrowernumber, firstname, surname, address, address2, city, zipcode, country, email, emailpro, B_email, smsalertnumber, phone, cardnumber, date_due
668
                if ( C4::Context->preference('OverdueNoticeCalendar') ) {
580
FROM   issues,borrowers,categories,items
669
                    $days_between = $calendar->days_between(
581
WHERE  issues.borrowernumber=borrowers.borrowernumber
670
                        dt_from_string( $data->{date_due} ),
582
AND    borrowers.categorycode=categories.categorycode
671
                        $date_to_run
583
AND    issues.itemnumber = items.itemnumber
672
                    );
584
AND    items.itemlost = 0
673
                } else {
585
AND    TO_DAYS($date)-TO_DAYS(issues.date_due) >= 0
674
                    $days_between =
586
END_SQL
675
                        $date_to_run->delta_days( dt_from_string( $data->{date_due} ) );
587
                my @borrower_parameters;
588
                if ($branchcode) {
589
                    if ($owning_library) {
590
                        $borrower_sql .= ' AND items.homebranch=? ';
591
                    } else {
592
                        $borrower_sql .= ' AND issues.branchcode=? ';
593
                    }
594
                    push @borrower_parameters, $branchcode;
595
                }
596
                if ($category) {
597
                    $borrower_sql .= ' AND borrowers.categorycode=? ';
598
                    push @borrower_parameters, $category;
599
                }
600
                $borrower_sql .= '  AND categories.overduenoticerequired=1 ORDER BY issues.borrowernumber';
601
602
                # $sth gets borrower info if at least one overdue item has triggered the overdue action.
603
                my $sth = $dbh->prepare($borrower_sql);
604
                $sth->execute(@borrower_parameters);
605
606
                if ( $verbose > 1 ) {
607
                    warn sprintf "--------Borrower SQL------\n";
608
                    warn $borrower_sql
609
                        . "\n $branchcode | "
610
                        . $category
611
                        . "\n ($mindays, $maxdays, "
612
                        . $date_to_run->datetime() . ")\n";
613
                    warn sprintf "--------------------------\n";
614
                }
676
                }
615
                $verbose and warn sprintf "Found %s borrowers with overdues\n", $sth->rows;
677
                $days_between = $days_between->in_units('days');
616
                my $borrowernumber;
678
                if ($listall) {
617
679
                    unless ( $days_between >= 1 and $days_between <= $MAX ) {
618
                # Iterate over all overdue issues
680
                        next;
619
                while ( my $data = $sth->fetchrow_hashref ) {
620
621
                    # check the borrower has at least one item that matches
622
                    my $days_between;
623
                    if ( C4::Context->preference('OverdueNoticeCalendar') ) {
624
                        $days_between = $calendar->days_between(
625
                            dt_from_string( $data->{date_due} ),
626
                            $date_to_run
627
                        );
628
                    } else {
629
                        $days_between =
630
                            $date_to_run->delta_days( dt_from_string( $data->{date_due} ) );
631
                    }
681
                    }
632
                    $days_between = $days_between->in_units('days');
682
                } else {
633
                    if ($triggered) {
683
                    if ($triggered) {
634
                        if ( $mindays != $days_between ) {
684
                        if ( $mindays != $days_between ) {
685
                            $verbose and warn "Overdue skipped for trigger $i\n";
635
                            next;
686
                            next;
636
                        }
687
                        }
637
                    } else {
688
                    } else {
638
                        unless ( $days_between >= $mindays
689
                        unless ( $days_between >= $mindays
639
                            && $days_between <= $maxdays )
690
                            && $days_between <= $maxdays )
640
                        {
691
                        {
692
                            $verbose and warn "Overdue skipped for trigger $i\n";
641
                            next;
693
                            next;
642
                        }
694
                        }
643
                    }
695
                    }
644
                    if ( defined $borrowernumber && $borrowernumber eq $data->{'borrowernumber'} ) {
696
                }
645
646
                        # we have already dealt with this borrower
647
                        $verbose and warn "already dealt with this borrower $borrowernumber";
648
                        next;
649
                    }
650
                    $borrowernumber = $data->{'borrowernumber'};
651
652
                    # Skip duplicate borrowers as we'll have already handled them
653
                    next if ( $patron_homelibrary && $already_queued{"$borrowernumber$i"} );
654
697
698
                if ($verbose) {
655
                    my $borr = sprintf(
699
                    my $borr = sprintf(
656
                        "%s%s%s (%s)",
700
                        "%s%s%s (%s)",
657
                        $data->{'surname'} || '',
701
                        $data->{'surname'} || '',
658
                        $data->{'firstname'} && $data->{'surname'} ? ', ' : '',
702
                        $data->{'firstname'} && $data->{'surname'} ? ', ' : '',
659
                        $data->{'firstname'} || '',
703
                        $data->{'firstname'} || '',
660
                        $borrowernumber
704
                        $data->{borrowernumber}
661
                    );
705
                    );
662
                    $verbose and warn "borrower $borr has items triggering level $i.\n";
706
                    warn sprintf "Overdue matched trigger %s with delay of %s days and overdue due date of %s\n",
663
707
                        $i,
664
                    my $patron = Koha::Patrons->find($borrowernumber);
708
                        $triggered, $overdue_rules->{ "overdue_$i" . '_delay' }, $data->{date_due};
665
                    if ($patron_homelibrary) {
709
                    warn sprintf "Using letter code '%s'\n",
666
                        $branchcode           = $patron->branchcode;
710
                        $overdue_rules->{ "overdue_$i" . '_notice' };
667
                        $library              = Koha::Libraries->find($branchcode);
711
                }
668
                        $admin_email_address  = $library->from_email_address;
669
                        $branch_email_address = C4::Context->preference('AddressForFailedOverdueNotices')
670
                            || $library->inbound_email_address;
671
                    }
672
                    @emails_to_use = ();
673
                    my $notice_email = $patron->notice_email_address;
674
                    unless ($nomail) {
675
                        if (@emails) {
676
                            foreach (@emails) {
677
                                push @emails_to_use, $data->{$_} if ( $data->{$_} );
678
                            }
679
                        } else {
680
                            push @emails_to_use, $notice_email if ($notice_email);
681
                        }
682
                    }
683
684
                    my $letter = Koha::Notice::Templates->find_effective_template(
685
                        {
686
                            module     => 'circulation',
687
                            code       => $overdue_rules->{ "overdue_$i" . '_notice' },
688
                            branchcode => $branchcode,
689
                            lang       => $patron->lang
690
                        }
691
                    );
692
693
                    unless ($letter) {
694
                        $verbose and warn qq|Message '$overdue_rules->{"overdue_$i"."_notice"}' content not found|;
695
696
                        # might as well skip while PERIOD, no other borrowers are going to work.
697
                        # FIXME : Does this mean a letter must be defined in order to trigger a debar ?
698
                        next PERIOD;
699
                    }
700
701
                    if ( $overdue_rules->{ "overdue_$i" . '_restrict' } ) {
702
703
                        #action taken is debarring
704
                        AddUniqueDebarment(
705
                            {
706
                                borrowernumber => $borrowernumber,
707
                                type           => 'OVERDUES',
708
                                comment        => "OVERDUES_PROCESS " . output_pref( dt_from_string() ),
709
                            }
710
                        ) unless $test_mode;
711
                        $verbose and warn "debarring $borr\n";
712
                    }
713
                    my @params = $patron_homelibrary ? ($borrowernumber) : ( $borrowernumber, $branchcode );
714
715
                    $sth2->execute(@params);
716
                    my $itemcount = 0;
717
                    my $titles    = "";
718
                    my @items     = ();
719
720
                    my $j                            = 0;
721
                    my $exceededPrintNoticesMaxLines = 0;
722
723
                    # Get each overdue item for this patron
724
                    while ( my $item_info = $sth2->fetchrow_hashref() ) {
725
                        if ( C4::Context->preference('OverdueNoticeCalendar') ) {
726
                            $days_between =
727
                                $calendar->days_between( dt_from_string( $item_info->{date_due} ), $date_to_run );
728
                        } else {
729
                            $days_between = $date_to_run->delta_days( dt_from_string( $item_info->{date_due} ) );
730
                        }
731
                        $days_between = $days_between->in_units('days');
732
                        if ($listall) {
733
                            unless ( $days_between >= 1 and $days_between <= $MAX ) {
734
                                next;
735
                            }
736
                        } else {
737
                            if ($triggered) {
738
                                if ( $mindays != $days_between ) {
739
                                    next;
740
                                }
741
                            } else {
742
                                unless ( $days_between >= $mindays
743
                                    && $days_between <= $maxdays )
744
                                {
745
                                    next;
746
                                }
747
                            }
748
                        }
749
750
                        if (   ( scalar(@emails_to_use) == 0 || $nomail )
751
                            && $PrintNoticesMaxLines
752
                            && $j >= $PrintNoticesMaxLines )
753
                        {
754
                            $exceededPrintNoticesMaxLines = 1;
755
                            last;
756
                        }
757
                        next if $patron_homelibrary and !grep { $seen{ $item_info->{branchcode} } } @branches;
758
                        $j++;
759
760
                        $titles .= C4::Letters::get_item_content(
761
                            { item => $item_info, item_content_fields => \@item_content_fields, dateonly => 1 } );
762
                        $itemcount++;
763
                        push @items, $item_info;
764
                    }
765
                    $sth2->finish;
766
767
                    my @message_transport_types = split( /,/, $overdue_rules->{ "overdue_$i" . '_mtt' } );
768
769
                    my $print_sent = 0;    # A print notice is not yet sent for this patron
770
                    for my $mtt (@message_transport_types) {
771
                        next if $mtt eq 'itiva';
772
                        my $effective_mtt = $mtt;
773
                        if (   ( $mtt eq 'email' and not scalar @emails_to_use )
774
                            or ( $mtt eq 'sms' and not $data->{smsalertnumber} ) )
775
                        {
776
                            # email or sms is requested but not exist, do a print.
777
                            $effective_mtt = 'print';
778
                        }
779
                        splice @items, $PrintNoticesMaxLines
780
                            if $effective_mtt eq 'print'
781
                            && $PrintNoticesMaxLines
782
                            && scalar @items > $PrintNoticesMaxLines;
783
784
                        #catch the case where we are sending a print to someone with an email
785
786
                        my $letter_exists = Koha::Notice::Templates->find_effective_template(
787
                            {
788
                                module                 => 'circulation',
789
                                code                   => $overdue_rules->{ "overdue_$i" . '_notice' },
790
                                message_transport_type => $effective_mtt,
791
                                branchcode             => $branchcode,
792
                                lang                   => $patron->lang
793
                            }
794
                        );
795
796
                        my $letter = parse_overdues_letter(
797
                            {
798
                                letter_code    => $overdue_rules->{ "overdue_$i" . '_notice' },
799
                                borrowernumber => $borrowernumber,
800
                                branchcode     => $branchcode,
801
                                items          => \@items,
802
                                substitute     =>
803
                                    {    # this appears to be a hack to overcome incomplete features in this code.
804
                                    bib             => $library->branchname,    # maybe 'bib' is a typo for 'lib<rary>'?
805
                                    'items.content' => $titles,
806
                                    'count'         => $itemcount,
807
                                    },
808
809
                                # If there is no template defined for the requested letter
810
                                # Fallback on the original type
811
                                message_transport_type => $letter_exists ? $effective_mtt : $mtt,
812
                            }
813
                        );
814
                        unless ( $letter && $letter->{content} ) {
815
                            $verbose and warn qq|Message '$overdue_rules->{"overdue_$i"."_notice"}' content not found|;
816
817
                            # this transport doesn't have a configured notice, so try another
818
                            next;
819
                        }
820
821
                        if ($exceededPrintNoticesMaxLines) {
822
                            $letter->{'content'} .=
823
                                "List too long for form; please check your account online for a complete list of your overdue items.";
824
                        }
825
826
                        my @misses = grep { /./ } map { /^([^>]*)[>]+/; ( $1 || '' ); } split /\</,
827
                            $letter->{'content'};
828
                        if (@misses) {
829
                            $verbose
830
                                and warn "The following terms were not matched and replaced: \n\t" . join "\n\t",
831
                                @misses;
832
                        }
833
712
834
                        if ($nomail) {
713
                my @message_transport_types = split( /,/, $overdue_rules->{ "overdue_$i" . '_mtt' } );
835
                            push @output_chunks,
714
                for my $mtt (@message_transport_types) {
836
                                prepare_letter_for_printing(
715
                    push @{ $borrower_overdues->{triggers}->{$i}->{ $overdue_rules->{ "overdue_$i" . '_notice' } }
837
                                {
716
                            ->{$mtt} }, $data;
838
                                    letter         => $letter,
839
                                    borrowernumber => $borrowernumber,
840
                                    firstname      => $data->{'firstname'},
841
                                    lastname       => $data->{'surname'},
842
                                    address1       => $data->{'address'},
843
                                    address2       => $data->{'address2'},
844
                                    city           => $data->{'city'},
845
                                    phone          => $data->{'phone'},
846
                                    cardnumber     => $data->{'cardnumber'},
847
                                    branchname     => $library->branchname,
848
                                    letternumber   => $i,
849
                                    postcode       => $data->{'zipcode'},
850
                                    country        => $data->{'country'},
851
                                    email          => $notice_email,
852
                                    itemcount      => $itemcount,
853
                                    titles         => $titles,
854
                                    outputformat   => defined $csvfilename ? 'csv'
855
                                    : defined $htmlfilename  ? 'html'
856
                                    : defined $text_filename ? 'text'
857
                                    : '',
858
                                }
859
                                );
860
                        } else {
861
                            if (   ( $mtt eq 'email' and not scalar @emails_to_use )
862
                                or ( $mtt eq 'sms' and not $data->{smsalertnumber} ) )
863
                            {
864
                                push @output_chunks,
865
                                    prepare_letter_for_printing(
866
                                    {
867
                                        letter         => $letter,
868
                                        borrowernumber => $borrowernumber,
869
                                        firstname      => $data->{'firstname'},
870
                                        lastname       => $data->{'surname'},
871
                                        address1       => $data->{'address'},
872
                                        address2       => $data->{'address2'},
873
                                        city           => $data->{'city'},
874
                                        postcode       => $data->{'zipcode'},
875
                                        country        => $data->{'country'},
876
                                        email          => $notice_email,
877
                                        itemcount      => $itemcount,
878
                                        titles         => $titles,
879
                                        outputformat   => defined $csvfilename ? 'csv'
880
                                        : defined $htmlfilename  ? 'html'
881
                                        : defined $text_filename ? 'text'
882
                                        : '',
883
                                    }
884
                                    );
885
                            }
886
                            unless ( $effective_mtt eq 'print' and $print_sent == 1 ) {
887
888
                                # Just sent a print if not already done.
889
                                C4::Letters::EnqueueLetter(
890
                                    {
891
                                        letter                 => $letter,
892
                                        borrowernumber         => $borrowernumber,
893
                                        message_transport_type => $effective_mtt,
894
                                        from_address           => $admin_email_address,
895
                                        to_address             => join( ',', @emails_to_use ),
896
                                        reply_address          => $library->inbound_email_address,
897
                                    }
898
                                ) unless $test_mode;
899
900
                                # A print notice should be sent only once per overdue level.
901
                                # Without this check, a print could be sent twice or more if the library checks sms and email and print and the patron has no email or sms number.
902
                                $print_sent = 1 if $effective_mtt eq 'print';
903
                            }
904
                        }
905
                    }
906
                    $already_queued{"$borrowernumber$i"} = 1;
907
                }
717
                }
908
                $sth->finish;
718
                if ( $overdue_rules->{ "overdue_$i" . '_restrict' } ) {
719
                    $borrower_overdues->{restrict} = 1;
720
                }
721
                $borrower_overdues->{'email'}          = $data->{'email'};
722
                $borrower_overdues->{'emailpro'}       = $data->{'emailpro'};
723
                $borrower_overdues->{'B_email'}        = $data->{'B_email'};
724
                $borrower_overdues->{'smsalertnumber'} = $data->{'smsalertnumber'};
725
                $borrower_overdues->{'phone'}          = $data->{'phone'};
909
            }
726
            }
910
        }
727
        }
728
        $sth->finish;
729
730
        # Catch final trigger
731
        if ( $borrower_overdues->{borrowernumber} ) {
732
            $verbose and warn "Collected overdue triggers for " . $borrower_overdues->{borrowernumber} . "\n";
733
            _enact_trigger($borrower_overdues);
734
            $borrower_overdues = {};
735
        }
911
    }
736
    }
912
737
913
    if (@output_chunks) {
738
    if (@output_chunks) {
Lines 964-971 END_SQL Link Here
964
            ) unless $test_mode;
789
            ) unless $test_mode;
965
        }
790
        }
966
    }
791
    }
967
968
}
792
}
793
969
if ($csvfilename) {
794
if ($csvfilename) {
970
795
971
    # note that we're not testing on $csv_fh to prevent closing
796
    # note that we're not testing on $csv_fh to prevent closing
Lines 984-989 if ( defined $htmlfilename ) { Link Here
984
809
985
These methods are internal to the operation of overdue_notices.pl.
810
These methods are internal to the operation of overdue_notices.pl.
986
811
812
=cut
813
814
sub _enact_trigger {
815
    my ($borrower_overdues) = @_;
816
817
    my $borrowernumber = $borrower_overdues->{borrowernumber};
818
    my $branchcode     = $borrower_overdues->{branchcode};
819
    my $patron         = Koha::Patrons->find($borrowernumber);
820
    my ( $library, $admin_email_address, $branch_email_address );
821
    $library = Koha::Libraries->find($branchcode);
822
823
    if ($patron_homelibrary) {
824
        $branchcode           = $patron->branchcode;
825
        $library              = Koha::Libraries->find($branchcode);
826
        $admin_email_address  = $library->from_email_address;
827
        $branch_email_address = C4::Context->preference('AddressForFailedOverdueNotices')
828
            || $library->inbound_email_address;
829
    }
830
    @emails_to_use = ();
831
    my $notice_email = $patron->notice_email_address;
832
    unless ($nomail) {
833
        if (@emails) {
834
            foreach (@emails) {
835
                push @emails_to_use, $borrower_overdues->{$_} if ( $borrower_overdues->{$_} );
836
            }
837
        } else {
838
            push @emails_to_use, $notice_email if ($notice_email);
839
        }
840
    }
841
842
    for my $trigger ( sort keys %{ $borrower_overdues->{triggers} } ) {
843
        for my $notice ( keys %{ $borrower_overdues->{triggers}->{$trigger} } ) {
844
            my $print_sent = 0;    # A print notice is not yet sent for this patron
845
            for my $mtt ( keys %{ $borrower_overdues->{triggers}->{$trigger}->{$notice} } ) {
846
847
                next if $mtt eq 'itiva';
848
                my $effective_mtt = $mtt;
849
                if (   ( $mtt eq 'email' and not scalar @emails_to_use )
850
                    or ( $mtt eq 'sms' and not $borrower_overdues->{smsalertnumber} ) )
851
                {
852
                    # email or sms is requested but not exist, do a print.
853
                    $effective_mtt = 'print';
854
                }
855
856
                my $j                            = 0;
857
                my $exceededPrintNoticesMaxLines = 0;
858
859
                # Get each overdue item for this trigger
860
                my $itemcount = 0;
861
                my $titles    = "";
862
                my @items     = ();
863
                for my $item_info ( @{ $borrower_overdues->{triggers}->{$trigger}->{$notice}->{$effective_mtt} } ) {
864
                    if (   ( scalar(@emails_to_use) == 0 || $nomail )
865
                        && $PrintNoticesMaxLines
866
                        && $j >= $PrintNoticesMaxLines )
867
                    {
868
                        $exceededPrintNoticesMaxLines = 1;
869
                        last;
870
                    }
871
                    next if $patron_homelibrary and !grep { $seen{ $item_info->{branchcode} } } @branches;
872
                    $j++;
873
874
                    $titles .= C4::Letters::get_item_content(
875
                        { item => $item_info, item_content_fields => \@item_content_fields, dateonly => 1 } );
876
                    $itemcount++;
877
                    push @items, $item_info;
878
                }
879
880
                splice @items, $PrintNoticesMaxLines
881
                    if $effective_mtt eq 'print'
882
                    && $PrintNoticesMaxLines
883
                    && scalar @items > $PrintNoticesMaxLines;
884
885
                #catch the case where we are sending a print to someone with an email
886
887
                my $letter_exists = Koha::Notice::Templates->find_effective_template(
888
                    {
889
                        module                 => 'circulation',
890
                        code                   => $notice,
891
                        message_transport_type => $effective_mtt,
892
                        branchcode             => $branchcode,
893
                        lang                   => $patron->lang
894
                    }
895
                );
896
897
                unless ($letter_exists) {
898
                    $verbose and warn qq|Message '$notice' for '$effective_mtt' content not found|;
899
                    next;
900
                }
901
902
                my $letter = parse_overdues_letter(
903
                    {
904
                        letter_code    => $notice,
905
                        borrowernumber => $borrowernumber,
906
                        branchcode     => $branchcode,
907
                        items          => \@items,
908
                        substitute     => {    # this appears to be a hack to overcome incomplete features in this code.
909
                            bib             => $library->branchname,    # maybe 'bib' is a typo for 'lib<rary>'?
910
                            'items.content' => $titles,
911
                            'count'         => $itemcount,
912
                        },
913
914
                        # If there is no template defined for the requested letter
915
                        # Fallback on the original type
916
                        message_transport_type => $letter_exists ? $effective_mtt : $mtt,
917
                    }
918
                );
919
                unless ( $letter && $letter->{content} ) {
920
                    $verbose and warn qq|Message '$notice' content not found|;
921
922
                    # this transport doesn't have a configured notice, so try another
923
                    next;
924
                }
925
926
                if ($exceededPrintNoticesMaxLines) {
927
                    $letter->{'content'} .=
928
                        "List too long for form; please check your account online for a complete list of your overdue items.";
929
                }
930
931
                my @misses = grep { /./ } map { /^([^>]*)[>]+/; ( $1 || '' ); } split /\</,
932
                    $letter->{'content'};
933
                if (@misses) {
934
                    $verbose
935
                        and warn "The following terms were not matched and replaced: \n\t" . join "\n\t",
936
                        @misses;
937
                }
938
939
                if ($nomail) {
940
                    push @output_chunks,
941
                        prepare_letter_for_printing(
942
                        {
943
                            letter         => $letter,
944
                            borrowernumber => $borrowernumber,
945
                            firstname      => $borrower_overdues->{'firstname'},
946
                            lastname       => $borrower_overdues->{'surname'},
947
                            address1       => $borrower_overdues->{'address'},
948
                            address2       => $borrower_overdues->{'address2'},
949
                            city           => $borrower_overdues->{'city'},
950
                            phone          => $borrower_overdues->{'phone'},
951
                            cardnumber     => $borrower_overdues->{'cardnumber'},
952
                            branchname     => $library->branchname,
953
                            letternumber   => $trigger,
954
                            postcode       => $borrower_overdues->{'zipcode'},
955
                            country        => $borrower_overdues->{'country'},
956
                            email          => $notice_email,
957
                            itemcount      => $itemcount,
958
                            titles         => $titles,
959
                            outputformat   => defined $csvfilename ? 'csv'
960
                            : defined $htmlfilename  ? 'html'
961
                            : defined $text_filename ? 'text'
962
                            : '',
963
                        }
964
                        );
965
                } else {
966
                    if (   ( $mtt eq 'email' and not scalar @emails_to_use )
967
                        or ( $mtt eq 'sms' and not $borrower_overdues->{smsalertnumber} ) )
968
                    {
969
                        push @output_chunks,
970
                            prepare_letter_for_printing(
971
                            {
972
                                letter         => $letter,
973
                                borrowernumber => $borrowernumber,
974
                                firstname      => $borrower_overdues->{'firstname'},
975
                                lastname       => $borrower_overdues->{'surname'},
976
                                address1       => $borrower_overdues->{'address'},
977
                                address2       => $borrower_overdues->{'address2'},
978
                                city           => $borrower_overdues->{'city'},
979
                                postcode       => $borrower_overdues->{'zipcode'},
980
                                country        => $borrower_overdues->{'country'},
981
                                email          => $notice_email,
982
                                itemcount      => $itemcount,
983
                                titles         => $titles,
984
                                outputformat   => defined $csvfilename ? 'csv'
985
                                : defined $htmlfilename  ? 'html'
986
                                : defined $text_filename ? 'text'
987
                                : '',
988
                            }
989
                            );
990
                    }
991
                    unless ( $effective_mtt eq 'print' and $print_sent == 1 ) {
992
993
                        # Just sent a print if not already done.
994
                        C4::Letters::EnqueueLetter(
995
                            {
996
                                letter                 => $letter,
997
                                borrowernumber         => $borrowernumber,
998
                                message_transport_type => $effective_mtt,
999
                                from_address           => $admin_email_address,
1000
                                to_address             => join( ',', @emails_to_use ),
1001
                                reply_address          => $library->inbound_email_address,
1002
                            }
1003
                        ) unless $test_mode;
1004
1005
                        # A print notice should be sent only once per overdue level.
1006
                        # Without this check, a print could be sent twice or more if the library checks sms and email and print and the patron has no email or sms number.
1007
                        $print_sent = 1 if $effective_mtt eq 'print';
1008
                    }
1009
                }
1010
            }
1011
1012
            $already_queued{"$borrowernumber$trigger"} = 1;
1013
        }
1014
    }
1015
1016
    if ( $borrower_overdues->{restrict} ) {
1017
1018
        #action taken is debarring
1019
        AddUniqueDebarment(
1020
            {
1021
                borrowernumber => $borrowernumber,
1022
                type           => 'OVERDUES',
1023
                comment        => "OVERDUES_PROCESS " . output_pref( dt_from_string() ),
1024
            }
1025
        ) unless $test_mode;
1026
1027
        my $borr = sprintf(
1028
            "%s%s%s (%s)",
1029
            $borrower_overdues->{'surname'} || '',
1030
            $borrower_overdues->{'firstname'} && $borrower_overdues->{'surname'} ? ', ' : '',
1031
            $borrower_overdues->{'firstname'} || '',
1032
            $borrower_overdues->{borrowernumber}
1033
        );
1034
        $verbose and warn "debarring $borr\n";
1035
    }
1036
}
1037
987
=head2 prepare_letter_for_printing
1038
=head2 prepare_letter_for_printing
988
1039
989
returns a string of text appropriate for printing in the event that an
1040
returns a string of text appropriate for printing in the event that an
990
- 

Return to bug 10190