Bug 17600: Standardize our EXPORT_OK
[srvgit] / misc / cronjobs / overdue_notices.pl
index 7504c7a..a1c2211 100755 (executable)
@@ -24,25 +24,27 @@ BEGIN {
 
     # find Koha's Perl modules
     # test carefully before changing this
-    use FindBin;
+    use FindBin ();
     eval { require "$FindBin::Bin/../kohalib.pl" };
 }
 
-use Getopt::Long;
-use Pod::Usage;
+use Getopt::Long qw( GetOptions );
+use Pod::Usage qw( pod2usage );
 use Text::CSV_XS;
 use DateTime;
 use DateTime::Duration;
 
+use Koha::Script -cron;
 use C4::Context;
 use C4::Letters;
-use C4::Overdues qw(GetFine GetOverdueMessageTransportTypes parse_overdues_letter);
-use C4::Log;
-use Koha::Patron::Debarments qw(AddUniqueDebarment);
-use Koha::DateUtils;
+use C4::Overdues qw( GetOverdueMessageTransportTypes parse_overdues_letter );
+use C4::Log qw( cronlogaction );
+use Koha::Patron::Debarments qw( AddUniqueDebarment );
+use Koha::DateUtils qw( dt_from_string output_pref );
 use Koha::Calendar;
 use Koha::Libraries;
 use Koha::Acquisition::Currencies;
+use Koha::Patrons;
 
 =head1 NAME
 
@@ -56,23 +58,26 @@ overdue_notices.pl
   [ -email <email_type> ... ]
 
  Options:
-   -help                          brief help message
-   -man                           full documentation
-   -v                             verbose
-   -n                             No email will be sent
-   -max          <days>           maximum days overdue to deal with
-   -library      <branchname>     only deal with overdues from this library (repeatable : several libraries can be given)
-   -csv          <filename>       populate CSV file
-   -html         <directory>      Output html to a file in the given directory
-   -text         <directory>      Output plain text to a file in the given directory
-   -itemscontent <list of fields> item information in templates
-   -borcat       <categorycode>   category code that must be included
-   -borcatout    <categorycode>   category code that must be excluded
-   -t                             only include triggered overdues
+   -help                          Brief help message.
+   -man                           Full documentation.
+   -v                             Verbose mode.
+   -n                             No email will be sent.
+   -max          <days>           Maximum days overdue to deal with.
+   -library      <branchcode>     Only deal with overdues from this library.
+                                  (repeatable : several libraries can be given)
+   -csv          <filename>       Populate CSV file.
+   -html         <directory>      Output html to a file in the given directory.
+   -text         <directory>      Output plain text to a file in the given directory.
+   -itemscontent <list of fields> Item information in templates.
+   -borcat       <categorycode>   Category code that must be included.
+   -borcatout    <categorycode>   Category code that must be excluded.
+   -t                             Only include triggered overdues.
    --test                         Run in test mode. No changes will be made on the DB.
-   -list-all                      list all overdues
-   -date         <yyyy-mm-dd>     emulate overdues run for this date
-   -email        <email_type>     type of email that will be used. Can be 'email', 'emailpro' or 'B_email'. Repeatable.
+   -list-all                      List all overdues.
+   -date         <yyyy-mm-dd>     Emulate overdues run for this date.
+   -email        <email_type>     Type of email that will be used.
+                                  Can be 'email', 'emailpro' or 'B_email'. Repeatable.
+   --frombranch                   Set the from address for the notice to one of 'item-homebranch' or 'item-issuebranch'.
 
 =head1 OPTIONS
 
@@ -181,6 +186,12 @@ use it in order to send overdues on a specific date and not Now. Format: YYYY-MM
 
 Allows to specify which type of email will be used. Can be email, emailpro or B_email. Repeatable.
 
+=item B<--frombranch>
+
+Use the address information from the item homebranch library instead of the issuing library.
+
+Defaults to 'item-issuebranch'
+
 =back
 
 =head1 DESCRIPTION
@@ -290,6 +301,7 @@ my $verbose = 0;
 my $nomail  = 0;
 my $MAX     = 90;
 my $test_mode = 0;
+my $frombranch = 'item-issuebranch';
 my @branchcodes; # Branch(es) passed as parameter
 my @emails_to_use;    # Emails to use for messaging
 my @emails;           # Emails given in command-line parameters
@@ -321,18 +333,23 @@ GetOptions(
     'borcat=s'       => \@myborcat,
     'borcatout=s'    => \@myborcatout,
     'email=s'        => \@emails,
+    'frombranch=s'   => \$frombranch,
 ) or pod2usage(2);
 pod2usage(1) if $help;
 pod2usage( -verbose => 2 ) if $man;
-
 cronlogaction() unless $test_mode;
 
 if ( defined $csvfilename && $csvfilename =~ /^-/ ) {
     warn qq(using "$csvfilename" as filename, that seems odd);
 }
 
-my @overduebranches    = C4::Overdues::GetBranchcodesWithOverdueRules();       # Branches with overdue rules
-my @branches;                                                                  # Branches passed as parameter with overdue rules
+die "--frombranch takes item-homebranch or item-issuebranch only"
+    unless ( $frombranch eq 'item-issuebranch'
+        || $frombranch eq 'item-homebranch' );
+my $owning_library = ( $frombranch eq 'item-homebranch' ) ? 1 : 0;
+
+my @overduebranches    = C4::Overdues::GetBranchcodesWithOverdueRules();    # Branches with overdue rules
+my @branches;                                    # Branches passed as parameter with overdue rules
 my $branchcount = scalar(@overduebranches);
 
 my $overduebranch_word = scalar @overduebranches > 1 ? 'branches' : 'branch';
@@ -356,8 +373,8 @@ if (@branchcodes) {
     
     if (@branches) {
 
-       my $branch_word = scalar @branches > 1 ? 'branches' : 'branch';
-       $verbose and warn "$branch_word @branches have overdue rules\n";
+        my $branch_word = scalar @branches > 1 ? 'branches' : 'branch';
+    $verbose and warn "$branch_word @branches have overdue rules\n";
 
     } else {
     
@@ -395,7 +412,7 @@ binmode( STDOUT, ':encoding(UTF-8)' );
 our $csv;       # the Text::CSV_XS object
 our $csv_fh;    # the filehandle to the CSV file.
 if ( defined $csvfilename ) {
-    my $sep_char = C4::Context->preference('delimiter') || ';';
+    my $sep_char = C4::Context->preference('CSVDelimiter') || ';';
     $sep_char = "\t" if ($sep_char eq 'tabulation');
     $csv = Text::CSV_XS->new( { binary => 1 , sep_char => $sep_char } );
     if ( $csvfilename eq '' ) {
@@ -416,7 +433,7 @@ if ( defined $htmlfilename ) {
   if ( $htmlfilename eq '' ) {
     $fh = *STDOUT;
   } else {
-    my $today = DateTime->now(time_zone => C4::Context->tz );
+    my $today = dt_from_string();
     open $fh, ">:encoding(UTF-8)",File::Spec->catdir ($htmlfilename,"notices-".$today->ymd().".html");
   }
   
@@ -437,27 +454,29 @@ elsif ( defined $text_filename ) {
   if ( $text_filename eq '' ) {
     $fh = *STDOUT;
   } else {
-    my $today = DateTime->now(time_zone => C4::Context->tz );
-    open $fh, ">",File::Spec->catdir ($text_filename,"notices-".$today->ymd().".txt");
+    my $today = dt_from_string();
+    open $fh, ">:encoding(UTF-8)",File::Spec->catdir ($text_filename,"notices-".$today->ymd().".txt");
   }
 }
 
 foreach my $branchcode (@branches) {
+    my $calendar;
     if ( C4::Context->preference('OverdueNoticeCalendar') ) {
-        my $calendar = Koha::Calendar->new( branchcode => $branchcode );
+        $calendar = Koha::Calendar->new( branchcode => $branchcode );
         if ( $calendar->is_holiday($date_to_run) ) {
             next;
         }
     }
 
     my $library             = Koha::Libraries->find($branchcode);
-    my $admin_email_address = $library->branchemail
-      || C4::Context->preference('KohaAdminEmailAddress');
+    my $admin_email_address = $library->from_email_address;
+    my $branch_email_address = C4::Context->preference('AddressForFailedOverdueNotices')
+      || $library->inbound_email_address;
     my @output_chunks;    # may be sent to mail or stdout or csv file.
 
-    $verbose and warn sprintf "branchcode : '%s' using %s\n", $branchcode, $admin_email_address;
+    $verbose and warn sprintf "branchcode : '%s' using %s\n", $branchcode, $branch_email_address;
 
-    my $sth2 = $dbh->prepare( <<"END_SQL" );
+    my $sql2 = <<"END_SQL";
 SELECT biblio.*, items.*, issues.*, biblioitems.itemtype, branchname
   FROM issues,items,biblio, biblioitems, branches b
   WHERE items.itemnumber=issues.itemnumber
@@ -465,9 +484,17 @@ SELECT biblio.*, items.*, issues.*, biblioitems.itemtype, branchname
     AND b.branchcode = items.homebranch
     AND biblio.biblionumber   = biblioitems.biblionumber
     AND issues.borrowernumber = ?
+    AND items.itemlost = 0
     AND TO_DAYS($date)-TO_DAYS(issues.date_due) >= 0
 END_SQL
 
+    if($owning_library) {
+      $sql2 .= ' AND items.homebranch = ? ';
+    } else {
+      $sql2 .= ' AND issues.branchcode = ? ';
+    }
+    my $sth2 = $dbh->prepare($sql2);
+
     my $query = "SELECT * FROM overduerules WHERE delay1 IS NOT NULL AND branchcode = ? ";
     $query .= " AND categorycode IN (".join( ',' , ('?') x @myborcat ).") " if (@myborcat);
     $query .= " AND categorycode NOT IN (".join( ',' , ('?') x @myborcatout ).") " if (@myborcatout);
@@ -508,19 +535,25 @@ END_SQL
             # $letter->{'content'} is the text of the mail that is sent.
             # this text contains fields that are replaced by their value. Those fields must be written between brackets
             # The following fields are available :
-           # 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).
+        # 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).
             # <date> <itemcount> <firstname> <lastname> <address1> <address2> <address3> <city> <postcode> <country>
 
             my $borrower_sql = <<"END_SQL";
 SELECT issues.borrowernumber, firstname, surname, address, address2, city, zipcode, country, email, emailpro, B_email, smsalertnumber, phone, cardnumber, date_due
-FROM   issues,borrowers,categories
+FROM   issues,borrowers,categories,items
 WHERE  issues.borrowernumber=borrowers.borrowernumber
 AND    borrowers.categorycode=categories.categorycode
+AND    issues.itemnumber = items.itemnumber
+AND    items.itemlost = 0
 AND    TO_DAYS($date)-TO_DAYS(issues.date_due) >= 0
 END_SQL
             my @borrower_parameters;
             if ($branchcode) {
-                $borrower_sql .= ' AND issues.branchcode=? ';
+        if($owning_library) {
+            $borrower_sql .= ' AND items.homebranch=? ';
+        } else {
+            $borrower_sql .= ' AND issues.branchcode=? ';
+        }
                 push @borrower_parameters, $branchcode;
             }
             if ( $overdue_rules->{categorycode} ) {
@@ -530,7 +563,7 @@ END_SQL
             $borrower_sql .= '  AND categories.overduenoticerequired=1 ORDER BY issues.borrowernumber';
 
             # $sth gets borrower info iff at least one overdue item has triggered the overdue action.
-               my $sth = $dbh->prepare($borrower_sql);
+            my $sth = $dbh->prepare($borrower_sql);
             $sth->execute(@borrower_parameters);
 
             $verbose and warn $borrower_sql . "\n $branchcode | " . $overdue_rules->{'categorycode'} . "\n ($mindays, $maxdays, ".  $date_to_run->datetime() .")\nreturns " . $sth->rows . " rows";
@@ -541,8 +574,6 @@ END_SQL
                 my $days_between;
                 if ( C4::Context->preference('OverdueNoticeCalendar') )
                 {
-                    my $calendar =
-                      Koha::Calendar->new( branchcode => $branchcode );
                     $days_between =
                       $calendar->days_between( dt_from_string($data->{date_due}),
                         $date_to_run );
@@ -570,16 +601,17 @@ END_SQL
                     next;
                 }
                 $borrowernumber = $data->{'borrowernumber'};
-                my $borr =
-                    $data->{'firstname'} . ', '
-                  . $data->{'surname'} . ' ('
-                  . $borrowernumber . ')';
+                my $borr = sprintf( "%s%s%s (%s)",
+                    $data->{'surname'} || '',
+                    $data->{'firstname'} && $data->{'surname'} ? ', ' : '',
+                    $data->{'firstname'} || '',
+                    $borrowernumber );
                 $verbose
                   and warn "borrower $borr has items triggering level $i.";
 
+                my $patron = Koha::Patrons->find( $borrowernumber );
                 @emails_to_use = ();
-                my $notice_email =
-                    C4::Members::GetNoticeEmailAddress($borrowernumber);
+                my $notice_email = $patron->notice_email_address;
                 unless ($nomail) {
                     if (@emails) {
                         foreach (@emails) {
@@ -591,7 +623,14 @@ END_SQL
                     }
                 }
 
-                my $letter = C4::Letters::getletter( 'circulation', $overdue_rules->{"letter$i"}, $branchcode );
+                my $letter = Koha::Notice::Templates->find_effective_template(
+                    {
+                        module     => 'circulation',
+                        code       => $overdue_rules->{"letter$i"},
+                        branchcode => $branchcode,
+                        lang       => $patron->lang
+                    }
+                );
 
                 unless ($letter) {
                     $verbose and warn qq|Message '$overdue_rules->{"letter$i"}' content not found|;
@@ -613,7 +652,7 @@ END_SQL
                     ) unless $test_mode;
                     $verbose and warn "debarring $borr\n";
                 }
-                my @params = ($borrowernumber);
+                my @params = ($borrowernumber,$branchcode);
                 $verbose and warn "STH2 PARAMS: borrowernumber = $borrowernumber";
 
                 $sth2->execute(@params);
@@ -625,8 +664,6 @@ END_SQL
                 my $exceededPrintNoticesMaxLines = 0;
                 while ( my $item_info = $sth2->fetchrow_hashref() ) {
                     if ( C4::Context->preference('OverdueNoticeCalendar') ) {
-                        my $calendar =
-                          Koha::Calendar->new( branchcode => $branchcode );
                         $days_between =
                           $calendar->days_between(
                             dt_from_string( $item_info->{date_due} ), $date_to_run );
@@ -662,11 +699,8 @@ END_SQL
                       last;
                     }
                     $j++;
-                    my @item_info = map { $_ =~ /^date|date$/ ?
-                                           eval { output_pref( { dt => dt_from_string( $item_info->{$_} ), dateonly => 1 } ); }
-                                           :
-                                           $item_info->{$_} || '' } @item_content_fields;
-                    $titles .= join("\t", @item_info) . "\n";
+
+                    $titles .= C4::Letters::get_item_content( { item => $item_info, item_content_fields => \@item_content_fields, dateonly => 1 } );
                     $itemcount++;
                     push @items, $item_info;
                 }
@@ -684,8 +718,19 @@ END_SQL
                         # email or sms is requested but not exist, do a print.
                         $effective_mtt = 'print';
                     }
+                    splice @items, $PrintNoticesMaxLines if $effective_mtt eq 'print' && $PrintNoticesMaxLines && scalar @items > $PrintNoticesMaxLines;
+                    #catch the case where we are sending a print to someone with an email
+
+                    my $letter_exists = Koha::Notice::Templates->find_effective_template(
+                        {
+                            module     => 'circulation',
+                            code       => $overdue_rules->{"letter$i"},
+                            message_transport_type => $effective_mtt,
+                            branchcode => $branchcode,
+                            lang       => $patron->lang
+                        }
+                    );
 
-                    my $letter_exists = C4::Letters::getletter( 'circulation', $overdue_rules->{"letter$i"}, $branchcode, $effective_mtt ) ? 1 : 0;
                     my $letter = parse_overdues_letter(
                         {   letter_code     => $overdue_rules->{"letter$i"},
                             borrowernumber  => $borrowernumber,
@@ -697,11 +742,11 @@ END_SQL
                                                 'count'         => $itemcount,
                                                },
                             # If there is no template defined for the requested letter
-                            # Fallback on email
-                            message_transport_type => $letter_exists ? $effective_mtt : 'email',
+                            # Fallback on the original type
+                            message_transport_type => $letter_exists ? $effective_mtt : $mtt,
                         }
                     );
-                    unless ($letter) {
+                    unless ($letter && $letter->{content}) {
                         $verbose and warn qq|Message '$overdue_rules->{"letter$i"}' content not found|;
                         # this transport doesn't have a configured notice, so try another
                         next;
@@ -796,7 +841,7 @@ END_SQL
         # Generate the content of the csv with headers
         my $content;
         if ( defined $csvfilename ) {
-            my $delimiter = C4::Context->preference('delimiter') || ';';
+            my $delimiter = C4::Context->preference('CSVDelimiter') || ';';
             $content = join($delimiter, qw(title name surname address1 address2 zipcode city country email itemcount itemsinfo due_date issue_date)) . "\n";
         }
         else {
@@ -814,12 +859,13 @@ END_SQL
             title   => 'Overdue Notices',
             content => 'These messages were not sent directly to the patrons.',
         };
+
         C4::Letters::EnqueueLetter(
             {   letter                 => $letter,
                 borrowernumber         => undef,
                 message_transport_type => 'email',
                 attachments            => [$attachment],
-                to_address             => $admin_email_address,
+                to_address             => $branch_email_address,
             }
         ) unless $test_mode;
     }