Merge remote-tracking branch 'origin/new/bug_7751'
[koha.git] / misc / cronjobs / advance_notices.pl
index 6877cdb..a090ecc 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 
 # Copyright 2008 LibLime
 #
@@ -13,9 +13,9 @@
 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 #
-# You should have received a copy of the GNU General Public License along with
-# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
-# Suite 330, Boston, MA  02111-1307 USA
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 =head1 NAME
 
@@ -54,6 +54,7 @@ use C4::Letters;
 use C4::Members;
 use C4::Members::Messaging;
 use C4::Overdues;
+use Koha::DateUtils;
 
 
 # These are defaults for command line options.
@@ -62,14 +63,14 @@ my $confirm;                                                        # -c: Confir
 my $nomail;                                                         # -n: No mail. Will not send any emails.
 my $mindays     = 0;                                                # -m: Maximum number of days in advance to send notices
 my $maxdays     = 30;                                               # -e: the End of the time period
-my $fromaddress = C4::Context->preference('KohaAdminEmailAddress'); # -f: From address for the emails
 my $verbose     = 0;                                                # -v: verbose
+my $itemscontent = join(',',qw( issuedate title barcode author ));
 
-GetOptions( 'c'    => \$confirm,
-            'n'    => \$nomail,
-            'm:i'  => \$maxdays,
-            'f:s'  => \$fromaddress,
-            'v'    => \$verbose,
+GetOptions( 'c'              => \$confirm,
+            'n'              => \$nomail,
+            'm:i'            => \$maxdays,
+            'v'              => \$verbose,
+            'itemscontent=s' => \$itemscontent,
        );
 my $usage = << 'ENDUSAGE';
 
@@ -78,12 +79,10 @@ patrons. It queues them in the message queue, which is processed by
 the process_message_queue.pl cronjob.
 See the comments in the script for directions on changing the script.
 This script has the following parameters :
-       -c Confirm and remove this help & warning
-        -m maximum number of days in advance to send advance notices.
-        -f from address for the emails. Defaults to KohaAdminEmailAddress system preference
-       -n send No mail. Instead, all mail messages are printed on screen. Usefull for testing purposes.
-        -v verbose
-
+    -c Confirm and remove this help & warning
+    -m maximum number of days in advance to send advance notices.
+    -n send No mail. Instead, all mail messages are printed on screen. Usefull for testing purposes.
+    -v verbose
 ENDUSAGE
 
 # Since advance notice options are not visible in the web-interface
@@ -107,6 +106,8 @@ unless ($confirm) {
        
 }
 
+# The fields that will be substituted into <<items.content>>
+my @item_content_fields = split(/,/,$itemscontent);
 
 warn 'getting upcoming due issues' if $verbose;
 my $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => $maxdays } );
@@ -117,112 +118,209 @@ warn 'found ' . scalar( @$upcoming_dues ) . ' issues' if $verbose;
 my $upcoming_digest;
 my $due_digest;
 
+my $dbh = C4::Context->dbh();
+my $sth = $dbh->prepare(<<'END_SQL');
+SELECT biblio.*, items.*, issues.*
+  FROM issues,items,biblio
+  WHERE items.itemnumber=issues.itemnumber
+    AND biblio.biblionumber=items.biblionumber
+    AND issues.borrowernumber = ?
+    AND issues.itemnumber = ?
+    AND (TO_DAYS(date_due)-TO_DAYS(NOW()) = ?)
+END_SQL
+
+my $admin_adress = C4::Context->preference('KohaAdminEmailAddress');
+
 UPCOMINGITEM: foreach my $upcoming ( @$upcoming_dues ) {
     warn 'examining ' . $upcoming->{'itemnumber'} . ' upcoming due items' if $verbose;
     # warn( Data::Dumper->Dump( [ $upcoming ], [ 'overdue' ] ) );
-    
+
+    my $from_address = $upcoming->{branchemail} || $admin_adress;
+
     my $letter;
     my $borrower_preferences;
     if ( 0 == $upcoming->{'days_until_due'} ) {
         # This item is due today. Send an 'item due' message.
         $borrower_preferences = C4::Members::Messaging::GetMessagingPreferences( { borrowernumber => $upcoming->{'borrowernumber'},
-                                                                                   message_name   => 'item due' } );
+                                                                                   message_name   => 'item_due' } );
         # warn( Data::Dumper->Dump( [ $borrower_preferences ], [ 'borrower_preferences' ] ) );
-        next DUEITEM unless $borrower_preferences;
+        next unless $borrower_preferences;
         
         if ( $borrower_preferences->{'wants_digest'} ) {
             # cache this one to process after we've run through all of the items.
-            $due_digest->{$upcoming->{'borrowernumber'}}++;
+            my $digest = $due_digest->{$upcoming->{'borrowernumber'}} ||= {};
+            $digest->{email} ||= $from_address;
+            $digest->{count}++;
         } else {
             my $biblio = C4::Biblio::GetBiblioFromItemNumber( $upcoming->{'itemnumber'} );
             my $letter_type = 'DUE';
-            $letter = C4::Letters::getletter( 'circulation', $letter_type );
-            die "no letter of type '$letter_type' found. Please see sample_notices.sql" unless $letter;
-        
-            $letter = parse_letter( { letter         => $letter,
+            $sth->execute($upcoming->{'borrowernumber'},$upcoming->{'itemnumber'},'0');
+            my $titles = "";
+            while ( my $item_info = $sth->fetchrow_hashref()) {
+              my @item_info = map { $_ =~ /^date|date$/ ? format_date($item_info->{$_}) : $item_info->{$_} || '' } @item_content_fields;
+              $titles .= join("\t",@item_info) . "\n";
+            }
+
+            ## Get branch info for borrowers home library.
+            $letter = parse_letter( { letter_code    => $letter_type,
                                       borrowernumber => $upcoming->{'borrowernumber'},
-                                      branchchode    => $upcoming->{'branchcode'},
-                                      biblionumber   => $biblio->{'biblionumber'} } );
+                                      branchcode     => $upcoming->{'branchcode'},
+                                      biblionumber   => $biblio->{'biblionumber'},
+                                      itemnumber     => $upcoming->{'itemnumber'},
+                                      substitute     => { 'items.content' => $titles }
+                                    } )
+              or die "no letter of type '$letter_type' found. Please see sample_notices.sql";
         }
     } else {
         $borrower_preferences = C4::Members::Messaging::GetMessagingPreferences( { borrowernumber => $upcoming->{'borrowernumber'},
-                                                                                   message_name   => 'advance notice' } );
+                                                                                   message_name   => 'advance_notice' } );
         # warn( Data::Dumper->Dump( [ $borrower_preferences ], [ 'borrower_preferences' ] ) );
         next UPCOMINGITEM unless $borrower_preferences && exists $borrower_preferences->{'days_in_advance'};
         next UPCOMINGITEM unless $borrower_preferences->{'days_in_advance'} == $upcoming->{'days_until_due'};
-        
+
         if ( $borrower_preferences->{'wants_digest'} ) {
             # cache this one to process after we've run through all of the items.
-            $upcoming_digest->{$upcoming->{'borrowernumber'}}++;
+            my $digest = $upcoming_digest->{$upcoming->{'borrowernumber'}} ||= {};
+            $digest->{email} ||= $from_address;
+            $digest->{count}++;
         } else {
             my $biblio = C4::Biblio::GetBiblioFromItemNumber( $upcoming->{'itemnumber'} );
             my $letter_type = 'PREDUE';
-            $letter = C4::Letters::getletter( 'circulation', $letter_type );
-            die "no letter of type '$letter_type' found. Please see sample_notices.sql" unless $letter;
-        
-            $letter = parse_letter( { letter         => $letter,
+            $sth->execute($upcoming->{'borrowernumber'},$upcoming->{'itemnumber'},$borrower_preferences->{'days_in_advance'});
+            my $titles = "";
+            while ( my $item_info = $sth->fetchrow_hashref()) {
+              my @item_info = map { $_ =~ /^date|date$/ ? format_date($item_info->{$_}) : $item_info->{$_} || '' } @item_content_fields;
+              $titles .= join("\t",@item_info) . "\n";
+            }
+
+            ## Get branch info for borrowers home library.
+            $letter = parse_letter( { letter_code    => $letter_type,
                                       borrowernumber => $upcoming->{'borrowernumber'},
-                                      branchchode    => $upcoming->{'branchcode'},
-                                      biblionumber   => $biblio->{'biblionumber'} } );
+                                      branchcode     => $upcoming->{'branchcode'},
+                                      biblionumber   => $biblio->{'biblionumber'},
+                                      itemnumber     => $upcoming->{'itemnumber'},
+                                      substitute     => { 'items.content' => $titles }
+                                    } )
+              or die "no letter of type '$letter_type' found. Please see sample_notices.sql";
         }
     }
 
     # If we have prepared a letter, send it.
-    if ( $letter ) {
-        foreach my $transport ( @{$borrower_preferences->{'transports'}} ) {
+    if ($letter) {
+      if ($nomail) {
+        local $, = "\f";
+        print $letter->{'content'};
+      }
+      else {
+        foreach my $transport ( keys %{$borrower_preferences->{'transports'}} ) {
             C4::Letters::EnqueueLetter( { letter                 => $letter,
                                           borrowernumber         => $upcoming->{'borrowernumber'},
+                                          from_address           => $from_address,
                                           message_transport_type => $transport } );
         }
+      }
     }
-    
 }
 
 
 # warn( Data::Dumper->Dump( [ $upcoming_digest ], [ 'upcoming_digest' ] ) );
 
 # Now, run through all the people that want digests and send them
-PATRON: while ( my ( $borrowernumber, $count ) = each %$upcoming_digest ) {
+
+$sth = $dbh->prepare(<<'END_SQL');
+SELECT biblio.*, items.*, issues.*
+  FROM issues,items,biblio
+  WHERE items.itemnumber=issues.itemnumber
+    AND biblio.biblionumber=items.biblionumber
+    AND issues.borrowernumber = ?
+    AND (TO_DAYS(date_due)-TO_DAYS(NOW()) = ?)
+END_SQL
+
+PATRON: while ( my ( $borrowernumber, $digest ) = each %$upcoming_digest ) {
+    my $count = $digest->{count};
+    my $from_address = $digest->{email};
+
     my $borrower_preferences = C4::Members::Messaging::GetMessagingPreferences( { borrowernumber => $borrowernumber,
-                                                                                  message_name   => 'advance notice' } );
+                                                                                  message_name   => 'advance_notice' } );
     # warn( Data::Dumper->Dump( [ $borrower_preferences ], [ 'borrower_preferences' ] ) );
     next PATRON unless $borrower_preferences; # how could this happen?
 
 
     my $letter_type = 'PREDUEDGST';
-    my $letter = C4::Letters::getletter( 'circulation', $letter_type );
-    die "no letter of type '$letter_type' found. Please see sample_notices.sql" unless $letter;
-    $letter = parse_letter( { letter         => $letter,
-                              borrowernumber => $borrowernumber,
-                              substitute     => { count => $count }
-                         } );
 
-    foreach my $transport ( @{$borrower_preferences->{'transports'}} ) {
+    $sth->execute($borrowernumber,$borrower_preferences->{'days_in_advance'});
+    my $titles = "";
+    while ( my $item_info = $sth->fetchrow_hashref()) {
+      my @item_info = map { $_ =~ /^date|date$/ ? format_date($item_info->{$_}) : $item_info->{$_} || '' } @item_content_fields;
+      $titles .= join("\t",@item_info) . "\n";
+    }
+
+    ## Get branch info for borrowers home library.
+    my %branch_info = get_branch_info( $borrowernumber );
+
+    my $letter = parse_letter( { letter_code    => $letter_type,
+                              borrowernumber => $borrowernumber,
+                              substitute     => { count => $count,
+                                                  'items.content' => $titles,
+                                                  %branch_info,
+                                                }
+                         } )
+      or die "no letter of type '$letter_type' found. Please see sample_notices.sql";
+    if ($nomail) {
+      local $, = "\f";
+      print $letter->{'content'};
+    }
+    else {
+      foreach my $transport ( keys %{$borrower_preferences->{'transports'}} ) {
         C4::Letters::EnqueueLetter( { letter                 => $letter,
                                       borrowernumber         => $borrowernumber,
+                                      from_address           => $from_address,
                                       message_transport_type => $transport } );
+      }
     }
 }
 
 # Now, run through all the people that want digests and send them
-PATRON: while ( my ( $borrowernumber, $count ) = each %$due_digest ) {
+PATRON: while ( my ( $borrowernumber, $digest ) = each %$due_digest ) {
+    my $count = $digest->{count};
+    my $from_address = $digest->{email};
+
     my $borrower_preferences = C4::Members::Messaging::GetMessagingPreferences( { borrowernumber => $borrowernumber,
-                                                                                  message_name   => 'item due' } );
+                                                                                  message_name   => 'item_due' } );
     # warn( Data::Dumper->Dump( [ $borrower_preferences ], [ 'borrower_preferences' ] ) );
     next PATRON unless $borrower_preferences; # how could this happen?
 
     my $letter_type = 'DUEDGST';
-    my $letter = C4::Letters::getletter( 'circulation', $letter_type );
-    die "no letter of type '$letter_type' found. Please see sample_notices.sql" unless $letter;
-    $letter = parse_letter( { letter         => $letter,
-                              borrowernumber => $borrowernumber,
-                              substitute     => { count => $count }
-                         } );
+    $sth->execute($borrowernumber,'0');
+    my $titles = "";
+    while ( my $item_info = $sth->fetchrow_hashref()) {
+      my @item_info = map { $_ =~ /^date|date$/ ? format_date($item_info->{$_}) : $item_info->{$_} || '' } @item_content_fields;
+      $titles .= join("\t",@item_info) . "\n";
+    }
+
+    ## Get branch info for borrowers home library.
+    my %branch_info = get_branch_info( $borrowernumber );
 
-    foreach my $transport ( @{$borrower_preferences->{'transports'}} ) {
+    my $letter = parse_letter( { letter_code    => $letter_type,
+                              borrowernumber => $borrowernumber,
+                              substitute     => { count => $count,
+                                                  'items.content' => $titles,
+                                                  %branch_info,
+                                                }
+                         } )
+      or die "no letter of type '$letter_type' found. Please see sample_notices.sql";
+
+    if ($nomail) {
+      local $, = "\f";
+      print $letter->{'content'};
+    }
+    else {
+      foreach my $transport ( keys %{$borrower_preferences->{'transports'}} ) {
         C4::Letters::EnqueueLetter( { letter                 => $letter,
                                       borrowernumber         => $borrowernumber,
+                                      from_address           => $from_address,
                                       message_transport_type => $transport } );
+      }
     }
 }
 
@@ -230,37 +328,60 @@ PATRON: while ( my ( $borrowernumber, $count ) = each %$due_digest ) {
 
 =head2 parse_letter
 
-
-
 =cut
 
 sub parse_letter {
     my $params = shift;
-    foreach my $required ( qw( letter borrowernumber ) ) {
+    foreach my $required ( qw( letter_code borrowernumber ) ) {
         return unless exists $params->{$required};
     }
 
-    if ( $params->{'substitute'} ) {
-        while ( my ($key, $replacedby) = each %{$params->{'substitute'}} ) {
-            my $replacefield = "<<$key>>";
-            
-            $params->{'letter'}->{title}   =~ s/$replacefield/$replacedby/g;
-            $params->{'letter'}->{content} =~ s/$replacefield/$replacedby/g;
-        }
+    my %table_params = ( 'borrowers' => $params->{'borrowernumber'} );
+
+    if ( my $p = $params->{'branchcode'} ) {
+        $table_params{'branches'} = $p;
+    }
+    if ( my $p = $params->{'itemnumber'} ) {
+        $table_params{'issues'} = $p;
+        $table_params{'items'} = $p;
+    }
+    if ( my $p = $params->{'biblionumber'} ) {
+        $table_params{'biblio'} = $p;
+        $table_params{'biblioitems'} = $p;
     }
 
-    C4::Letters::parseletter( $params->{'letter'}, 'borrowers',   $params->{'borrowernumber'} );
+    return C4::Letters::GetPreparedLetter (
+        module => 'circulation',
+        letter_code => $params->{'letter_code'},
+        branchcode => $table_params{'branches'},
+        substitute => $params->{'substitute'},
+        tables     => \%table_params,
+    );
+}
 
-    if ( $params->{'branchcode'} ) {
-        C4::Letters::parseletter( $params->{'letter'}, 'branches',    $params->{'branchcode'} );
-    }
-    
-    if ( $params->{'biblionumber'} ) {
-        C4::Letters::parseletter( $params->{'letter'}, 'biblio',      $params->{'biblionumber'} );
-        C4::Letters::parseletter( $params->{'letter'}, 'biblioitems', $params->{'biblionumber'} );
+sub format_date {
+    my $date_string = shift;
+    my $dt=dt_from_string($date_string);
+    return output_pref($dt);
+}
+
+=head2 get_branch_info
+
+=cut
+
+sub get_branch_info {
+    my ( $borrowernumber ) = @_;
+
+    ## Get branch info for borrowers home library.
+    my $borrower_details = C4::Members::GetMember( borrowernumber => $borrowernumber );
+    my $borrower_branchcode = $borrower_details->{'branchcode'};
+    my $branch = C4::Branch::GetBranchDetail( $borrower_branchcode );
+    my %branch_info;
+    foreach my $key( keys %$branch ) {
+        $branch_info{"branches.$key"} = $branch->{$key};
     }
 
-    return $params->{'letter'};
+    return %branch_info;
 }
 
 1;