Bug 9987: Remove DB field aqorders.biblioitemnunmber
[koha.git] / C4 / Letters.pm
index 5a929d0..d36c29f 100644 (file)
@@ -29,6 +29,7 @@ use C4::Branch;
 use C4::Log;
 use C4::SMS;
 use C4::Debug;
+use Koha::DateUtils;
 use Date::Calc qw( Add_Delta_Days );
 use Encode;
 use Carp;
@@ -38,7 +39,7 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
 BEGIN {
        require Exporter;
        # set the version for version checking
-       $VERSION = 3.01;
+    $VERSION = 3.07.00.049;
        @ISA = qw(Exporter);
        @EXPORT = qw(
        &GetLetters &GetPreparedLetter &GetWrappedLetter &addalert &getalert &delalert &findrelatedto &SendAlerts &GetPrintMessages
@@ -93,7 +94,7 @@ $template->param(LETTERLOOP => \@letterloop);
 
 =cut
 
-sub GetLetters (;$) {
+sub GetLetters {
 
     # returns a reference to a hash of references to ALL letters...
     my $cat = shift;
@@ -116,11 +117,21 @@ sub GetLetters (;$) {
     return \%letters;
 }
 
-my %letter;
-sub getletter ($$$) {
+# FIXME: using our here means that a Plack server will need to be
+#        restarted fairly regularly when working with this routine.
+#        A better option would be to use Koha::Cache and use a cache
+#        that actually works in a persistent environment, but as a
+#        short-term fix, our will work.
+our %letter;
+sub getletter {
     my ( $module, $code, $branchcode ) = @_;
 
-    if (C4::Context->preference('IndependantBranches') && $branchcode){
+    $branchcode ||= '';
+
+    if ( C4::Context->preference('IndependentBranches')
+            and $branchcode
+            and C4::Context->userenv ) {
+
         $branchcode = C4::Context->userenv->{'branch'};
     }
 
@@ -149,7 +160,7 @@ sub getletter ($$$) {
 
 =cut
 
-sub addalert ($$$) {
+sub addalert {
     my ( $borrowernumber, $type, $externalid ) = @_;
     my $dbh = C4::Context->dbh;
     my $sth =
@@ -170,7 +181,7 @@ sub addalert ($$$) {
 
 =cut
 
-sub delalert ($) {
+sub delalert {
     my $alertid = shift or die "delalert() called without valid argument (alertid)";    # it's gonna die anyway.
     $debug and warn "delalert: deleting alertid $alertid";
     my $sth = C4::Context->dbh->prepare("delete from alert where alertid=?");
@@ -187,7 +198,7 @@ sub delalert ($) {
 
 =cut
 
-sub getalert (;$$$) {
+sub getalert {
     my ( $borrowernumber, $type, $externalid ) = @_;
     my $dbh   = C4::Context->dbh;
     my $query = "SELECT a.*, b.branchcode FROM alert a JOIN borrowers b USING(borrowernumber) WHERE";
@@ -224,16 +235,16 @@ sub getalert (;$$$) {
 # outmoded POD:
 # When type=virtual, the id is related to a virtual shelf and this sub returns the name of the sub
 
-sub findrelatedto ($$) {
-    my $type       = shift or return undef;
-    my $externalid = shift or return undef;
+sub findrelatedto {
+    my $type       = shift or return;
+    my $externalid = shift or return;
     my $q = ($type eq 'issue'   ) ?
 "select title as result from subscription left join biblio on subscription.biblionumber=biblio.biblionumber where subscriptionid=?" :
             ($type eq 'borrower') ?
 "select concat(firstname,' ',surname) from borrowers where borrowernumber=?" : undef;
     unless ($q) {
         warn "findrelatedto(): Illegal type '$type'";
-        return undef;
+        return;
     }
     my $sth = C4::Context->dbh->prepare($q);
     $sth->execute($externalid);
@@ -307,16 +318,18 @@ sub SendAlerts {
         # search the biblionumber
         my $strsth =  $type eq 'claimacquisition'
             ? qq{
-            SELECT aqorders.*,aqbasket.*,biblio.*,biblioitems.*,aqbooksellers.*
+            SELECT aqorders.*,aqbasket.*,biblio.*,biblioitems.*,aqbooksellers.*,
+            aqbooksellers.id AS booksellerid
             FROM aqorders
             LEFT JOIN aqbasket ON aqbasket.basketno=aqorders.basketno
             LEFT JOIN biblio ON aqorders.biblionumber=biblio.biblionumber
-            LEFT JOIN biblioitems ON aqorders.biblioitemnumber=biblioitems.biblioitemnumber
+            LEFT JOIN biblioitems ON aqorders.biblionumber=biblioitems.biblionumber
             LEFT JOIN aqbooksellers ON aqbasket.booksellerid=aqbooksellers.id
             WHERE aqorders.ordernumber IN (
             }
             : qq{
-            SELECT serial.*,subscription.*, biblio.*, aqbooksellers.*
+            SELECT serial.*,subscription.*, biblio.*, aqbooksellers.*,
+            aqbooksellers.id AS booksellerid
             FROM serial
             LEFT JOIN subscription ON serial.subscriptionid=subscription.subscriptionid
             LEFT JOIN biblio ON serial.biblionumber=biblio.biblionumber
@@ -356,7 +369,7 @@ sub SendAlerts {
 
         # ... then send mail
         my %mail = (
-            To => join( ','. @email),
+            To => join( ',', @email),
             From           => $userenv->{emailaddress},
             Subject        => Encode::encode( "utf8", "" . $letter->{title} ),
             Message        => Encode::encode( "utf8", "" . $letter->{content} ),
@@ -452,6 +465,9 @@ sub GetPreparedLetter {
        }
     }
 
+    my $OPACBaseURL = C4::Context->preference('OPACBaseURL');
+    $letter->{content} =~ s/<<OPACBaseURL>>/$OPACBaseURL/go;
+
     if ($want_librarian) {
         # parsing librarian name
         my $userenv = C4::Context->userenv;
@@ -540,37 +556,41 @@ sub _substitute_tables {
     }
 }
 
-my %handles = ();
 sub _parseletter_sth {
     my $table = shift;
+    my $sth;
     unless ($table) {
         carp "ERROR: _parseletter_sth() called without argument (table)";
         return;
     }
-    # check cache first
-    (defined $handles{$table}) and return $handles{$table};
+    # NOTE: we used to check whether we had a statement handle cached in
+    #       a %handles module-level variable. This was a dumb move and
+    #       broke things for the rest of us. prepare_cached is a better
+    #       way to cache statement handles anyway.
     my $query = 
-    ($table eq 'biblio'       ) ? "SELECT * FROM $table WHERE   biblionumber = ?"                      :
-    ($table eq 'biblioitems'  ) ? "SELECT * FROM $table WHERE   biblionumber = ?"                      :
-    ($table eq 'items'        ) ? "SELECT * FROM $table WHERE     itemnumber = ?"                      :
-    ($table eq 'issues'       ) ? "SELECT * FROM $table WHERE     itemnumber = ?"                      :
-    ($table eq 'reserves'     ) ? "SELECT * FROM $table WHERE borrowernumber = ? and biblionumber = ?" :
-    ($table eq 'borrowers'    ) ? "SELECT * FROM $table WHERE borrowernumber = ?"                      :
-    ($table eq 'branches'     ) ? "SELECT * FROM $table WHERE     branchcode = ?"                      :
-    ($table eq 'suggestions'  ) ? "SELECT * FROM $table WHERE   suggestionid = ?"                      :
-    ($table eq 'aqbooksellers') ? "SELECT * FROM $table WHERE             id = ?"                      :
-    ($table eq 'aqorders'     ) ? "SELECT * FROM $table WHERE    ordernumber = ?"                      :
-    ($table eq 'opac_news'    ) ? "SELECT * FROM $table WHERE          idnew = ?"                      :
+    ($table eq 'biblio'       ) ? "SELECT * FROM $table WHERE   biblionumber = ?"                                  :
+    ($table eq 'biblioitems'  ) ? "SELECT * FROM $table WHERE   biblionumber = ?"                                  :
+    ($table eq 'items'        ) ? "SELECT * FROM $table WHERE     itemnumber = ?"                                  :
+    ($table eq 'issues'       ) ? "SELECT * FROM $table WHERE     itemnumber = ?"                                  :
+    ($table eq 'old_issues'   ) ? "SELECT * FROM $table WHERE     itemnumber = ? ORDER BY timestamp DESC LIMIT 1"  :
+    ($table eq 'reserves'     ) ? "SELECT * FROM $table WHERE borrowernumber = ? and biblionumber = ?"             :
+    ($table eq 'borrowers'    ) ? "SELECT * FROM $table WHERE borrowernumber = ?"                                  :
+    ($table eq 'branches'     ) ? "SELECT * FROM $table WHERE     branchcode = ?"                                  :
+    ($table eq 'suggestions'  ) ? "SELECT * FROM $table WHERE   suggestionid = ?"                                  :
+    ($table eq 'aqbooksellers') ? "SELECT * FROM $table WHERE             id = ?"                                  :
+    ($table eq 'aqorders'     ) ? "SELECT * FROM $table WHERE    ordernumber = ?"                                  :
+    ($table eq 'opac_news'    ) ? "SELECT * FROM $table WHERE          idnew = ?"                                  :
+    ($table eq 'borrower_modifications') ? "SELECT * FROM $table WHERE borrowernumber = ? OR verification_token =?":
     undef ;
     unless ($query) {
         warn "ERROR: No _parseletter_sth query for table '$table'";
         return;     # nothing to get
     }
-    unless ($handles{$table} = C4::Context->dbh->prepare($query)) {
+    unless ($sth = C4::Context->dbh->prepare_cached($query)) {
         warn "ERROR: Failed to prepare query: '$query'";
         return;
     }
-    return $handles{$table};    # now cache is populated for that $table
+    return $sth;    # now cache is populated for that $table
 }
 
 =head2 _parseletter($letter, $table, $values)
@@ -584,40 +604,33 @@ sub _parseletter_sth {
 
 =cut
 
-my %columns = ();
 sub _parseletter {
     my ( $letter, $table, $values ) = @_;
 
-    # TEMPORARY hack until the expirationdate column is added to reserves
     if ( $table eq 'reserves' && $values->{'waitingdate'} ) {
         my @waitingdate = split /-/, $values->{'waitingdate'};
 
-        $values->{'expirationdate'} = C4::Dates->new(
-            sprintf(
-                '%04d-%02d-%02d',
-                Add_Delta_Days( @waitingdate, C4::Context->preference( 'ReservesMaxPickUpDelay' ) )
-            ),
-            'iso'
-        )->output();
+        my $dt = dt_from_string();
+        $dt->add( days => C4::Context->preference('ReservesMaxPickUpDelay') );
+        $values->{'expirationdate'} = output_pref( $dt, undef, 1 );
+
+        $values->{'waitingdate'} = output_pref( dt_from_string( $values->{'waitingdate'} ), undef, 1 );
+
     }
 
     if ($letter->{content} && $letter->{content} =~ /<<today>>/) {
-        my @da = localtime();
-        my $todaysdate = "$da[2]:$da[1]  " . C4::Dates->today();
+        my $todaysdate = output_pref( DateTime->now() );
         $letter->{content} =~ s/<<today>>/$todaysdate/go;
     }
 
-    # and get all fields from the table
-#   my $columns = $columns{$table};
-#   unless ($columns) {
-#       $columns = $columns{$table} =  C4::Context->dbh->selectcol_arrayref("SHOW COLUMNS FROM $table");
-#   }
-#   foreach my $field (@$columns) {
-
     while ( my ($field, $val) = each %$values ) {
         my $replacetablefield = "<<$table.$field>>";
         my $replacefield = "<<$field>>";
-        $val =~ s/\p{P}(?=$)//g if $val;
+        $val =~ s/\p{P}$// if $val && $table=~/biblio/;
+            #BZ 9886: Assuming that we want to eliminate ISBD punctuation here
+            #Therefore adding the test on biblio. This includes biblioitems,
+            #but excludes items. Removed unneeded global and lookahead.
+
         my $replacedby   = defined ($val) ? $val : '';
         ($letter->{title}  ) and do {
             $letter->{title}   =~ s/$replacetablefield/$replacedby/g;
@@ -659,17 +672,24 @@ places a letter in the message_queue database table, which will
 eventually get processed (sent) by the process_message_queue.pl
 cronjob when it calls SendQueuedMessages.
 
-return true on success
+return message_id on success
 
 =cut
 
-sub EnqueueLetter ($) {
-    my $params = shift or return undef;
+sub EnqueueLetter {
+    my $params = shift or return;
 
     return unless exists $params->{'letter'};
-    return unless exists $params->{'borrowernumber'};
+#   return unless exists $params->{'borrowernumber'};
     return unless exists $params->{'message_transport_type'};
 
+    my $content = $params->{letter}->{content};
+    $content =~ s/\s+//g if(defined $content);
+    if ( not defined $content or $content eq '' ) {
+        warn "Trying to add an empty message to the message queue" if $debug;
+        return;
+    }
+
     # If we have any attachments we should encode then into the body.
     if ( $params->{'attachments'} ) {
         $params->{'letter'} = _add_attachments(
@@ -701,7 +721,7 @@ ENDSQL
         $params->{'from_address'},                # from_address
         $params->{'letter'}->{'content-type'},    # content_type
     );
-    return $result;
+    return $dbh->last_insert_id(undef,undef,'message_queue', undef);
 }
 
 =head2 SendQueuedMessages ([$hashref]) 
@@ -714,7 +734,7 @@ returns number of messages sent.
 
 =cut
 
-sub SendQueuedMessages (;$) {
+sub SendQueuedMessages {
     my $params = shift;
 
     my $unsent_messages = _get_unsent_messages();
@@ -769,7 +789,8 @@ sub GetPrintMessages {
     my $params = shift || {};
     
     return _get_unsent_messages( { message_transport_type => 'print',
-                                   borrowernumber         => $params->{'borrowernumber'}, } );
+                                   borrowernumber         => $params->{'borrowernumber'},
+                                 } );
 }
 
 =head2 GetQueuedMessages ([$hashref])
@@ -859,13 +880,14 @@ sub _add_attachments {
 
 }
 
-sub _get_unsent_messages (;$) {
+sub _get_unsent_messages {
     my $params = shift;
 
     my $dbh = C4::Context->dbh();
     my $statement = << 'ENDSQL';
-SELECT message_id, borrowernumber, subject, content, message_transport_type, status, time_queued, from_address, to_address, content_type
-  FROM message_queue
+SELECT mq.message_id, mq.borrowernumber, mq.subject, mq.content, mq.message_transport_type, mq.status, mq.time_queued, mq.from_address, mq.to_address, mq.content_type, b.branchcode
+  FROM message_queue mq
+  LEFT JOIN borrowers b ON b.borrowernumber = mq.borrowernumber
  WHERE status = ?
 ENDSQL
 
@@ -884,6 +906,7 @@ ENDSQL
             push @query_params, $params->{'limit'};
         }
     }
+
     $debug and warn "_get_unsent_messages SQL: $statement";
     $debug and warn "_get_unsent_messages params: " . join(',',@query_params);
     my $sth = $dbh->prepare( $statement );
@@ -891,7 +914,7 @@ ENDSQL
     return $sth->fetchall_arrayref({});
 }
 
-sub _send_message_by_email ($;$$$) {
+sub _send_message_by_email {
     my $message = shift or return;
     my ($username, $password, $method) = @_;
 
@@ -904,13 +927,7 @@ sub _send_message_by_email ($;$$$) {
                                    status     => 'failed' } );
             return;
         }
-        my $which_address = C4::Context->preference('AutoEmailPrimaryAddress');
-        # If the system preference is set to 'first valid' (value == OFF), look up email address
-        if ($which_address eq 'OFF') {
-            $to_address = GetFirstValidEmailAddress( $message->{'borrowernumber'} );
-        } else {
-            $to_address = $member->{$which_address};
-        }
+        $to_address = C4::Members::GetNoticeEmailAddress( $message->{'borrowernumber'} );
         unless ($to_address) {  
             # warn "FAIL: No 'to_address' and no email for " . ($member->{surname} ||'') . ", borrowernumber ($message->{borrowernumber})";
             # warning too verbose for this more common case?
@@ -973,8 +990,8 @@ $content
 EOS
 }
 
-sub _send_message_by_sms ($) {
-    my $message = shift or return undef;
+sub _send_message_by_sms {
+    my $message = shift or return;
     my $member = C4::Members::GetMember( 'borrowernumber' => $message->{'borrowernumber'} );
     return unless $member->{'smsalertnumber'};
 
@@ -992,11 +1009,11 @@ sub _update_message_to_address {
     $dbh->do('UPDATE message_queue SET to_address=? WHERE message_id=?',undef,($to,$id));
 }
 
-sub _set_message_status ($) {
-    my $params = shift or return undef;
+sub _set_message_status {
+    my $params = shift or return;
 
     foreach my $required_parameter ( qw( message_id status ) ) {
-        return undef unless exists $params->{ $required_parameter };
+        return unless exists $params->{ $required_parameter };
     }
 
     my $dbh = C4::Context->dbh();