Bug 13157: UNIMARC holdingbranch facet is 995$c not 995$b
[koha.git] / C4 / Letters.pm
index 51020b6..0833ffa 100644 (file)
@@ -34,6 +34,7 @@ use Koha::DateUtils;
 use Date::Calc qw( Add_Delta_Days );
 use Encode;
 use Carp;
+use Koha::Email;
 
 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
 
@@ -62,59 +63,33 @@ C4::Letters - Give functions for Letters management
 
   Letters are managed through "alerts" sent by Koha on some events. All "alert" related functions are in this module too.
 
-=head2 GetLetters([$category])
+=head2 GetLetters([$module])
 
-  $letters = &GetLetters($category);
+  $letters = &GetLetters($module);
   returns informations about letters.
-  if needed, $category filters for letters given category
-  Create a letter selector with the following code
-
-=head3 in PERL SCRIPT
-
-my $letters = GetLetters($cat);
-my @letterloop;
-foreach my $thisletter (keys %$letters) {
-    my $selected = 1 if $thisletter eq $letter;
-    my %row =(
-        value => $thisletter,
-        selected => $selected,
-        lettername => $letters->{$thisletter},
-    );
-    push @letterloop, \%row;
-}
-$template->param(LETTERLOOP => \@letterloop);
-
-=head3 in TEMPLATE
-
-    <select name="letter">
-        <option value="">Default</option>
-    <!-- TMPL_LOOP name="LETTERLOOP" -->
-        <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="lettername" --></option>
-    <!-- /TMPL_LOOP -->
-    </select>
+  if needed, $module filters for letters given module
 
 =cut
 
 sub GetLetters {
+    my ($filters) = @_;
+    my $module    = $filters->{module};
+    my $code      = $filters->{code};
+    my $dbh       = C4::Context->dbh;
+    my $letters   = $dbh->selectall_arrayref(
+        q|
+            SELECT module, code, branchcode, name
+            FROM letter
+            WHERE 1
+        |
+          . ( $module ? q| AND module = ?| : q|| )
+          . ( $code   ? q| AND code = ?|   : q|| )
+          . q| GROUP BY code ORDER BY name|, { Slice => {} }
+        , ( $module ? $module : () )
+        , ( $code ? $code : () )
+    );
 
-    # returns a reference to a hash of references to ALL letters...
-    my ( $cat, $message_transport_type ) = @_;
-    $message_transport_type ||= 'email';
-    my %letters;
-    my $dbh = C4::Context->dbh;
-    my $sth;
-    my $query = q{
-        SELECT * FROM letter WHERE
-    };
-    $query .= q{ module = ? AND } if defined $cat;
-    $query .= q{ message_transport_type = ? ORDER BY name};
-    $sth = $dbh->prepare($query);
-    $sth->execute((defined $cat ? $cat : ()), $message_transport_type);
-
-    while ( my $letter = $sth->fetchrow_hashref ) {
-        $letters{ $letter->{'code'} } = $letter->{'name'};
-    }
-    return \%letters;
+    return $letters;
 }
 
 # FIXME: using our here means that a Plack server will need to be
@@ -229,12 +204,12 @@ sub getalert {
 
 =head2 findrelatedto($type, $externalid)
 
-       parameters :
-       - $type : the type of alert
-       - $externalid : the id of the "object" to query
-       
-       In the table alert, a "id" is stored in the externalid field. This "id" is related to another table, depending on the type of the alert.
-       When type=issue, the id is related to a subscriptionid and this sub returns the name of the biblio.
+    parameters :
+    - $type : the type of alert
+    - $externalid : the id of the "object" to query
+
+    In the table alert, a "id" is stored in the externalid field. This "id" is related to another table, depending on the type of the alert.
+    When type=issue, the id is related to a subscriptionid and this sub returns the name of the biblio.
 
 =cut
     
@@ -288,11 +263,10 @@ sub SendAlerts {
         # find the list of borrowers to alert
         my $alerts = getalert( '', 'issue', $externalid );
         foreach (@$alerts) {
-
             my $borinfo = C4::Members::GetMember('borrowernumber' => $_->{'borrowernumber'});
             my $email = $borinfo->{email} or next;
 
-            #          warn "sending issues...";
+#                    warn "sending issues...";
             my $userenv = C4::Context->userenv;
             my $branchdetails = GetBranchDetail($_->{'branchcode'});
             my $letter = GetPreparedLetter (
@@ -309,13 +283,20 @@ sub SendAlerts {
             ) or return;
 
             # ... then send mail
-            my %mail = (
-                To      => $email,
-                From    => $branchdetails->{'branchemail'} || C4::Context->preference("KohaAdminEmailAddress"),
-                Subject => Encode::encode( "utf8", "" . $letter->{title} ),
-                Message => Encode::encode( "utf8", "" . $letter->{content} ),
-                'Content-Type' => 'text/plain; charset="utf8"',
-                );
+            my $message = Koha::Email->new();
+            my %mail = $message->create_message_headers(
+                {
+                    to      => $email,
+                    from    => $branchdetails->{'branchemail'},
+                    replyto => $branchdetails->{'branchreplyto'},
+                    sender  => $branchdetails->{'branchreturnpath'},
+                    subject => Encode::encode( "utf8", "" . $letter->{title} ),
+                    message =>
+                      Encode::encode( "utf8", "" . $letter->{content} ),
+                    contenttype => 'text/plain; charset="utf8"',
+
+                }
+            );
             sendmail(%mail) or carp $Mail::Sendmail::error;
         }
     }
@@ -325,13 +306,11 @@ sub SendAlerts {
         # search the biblionumber
         my $strsth =  $type eq 'claimacquisition'
             ? qq{
-            SELECT aqorders.*,aqbasket.*,biblio.*,biblioitems.*,aqbooksellers.*,
-            aqbooksellers.id AS booksellerid
+            SELECT aqorders.*,aqbasket.*,biblio.*,biblioitems.*
             FROM aqorders
             LEFT JOIN aqbasket ON aqbasket.basketno=aqorders.basketno
             LEFT JOIN biblio ON aqorders.biblionumber=biblio.biblionumber
             LEFT JOIN biblioitems ON aqorders.biblionumber=biblioitems.biblionumber
-            LEFT JOIN aqbooksellers ON aqbasket.booksellerid=aqbooksellers.id
             WHERE aqorders.ordernumber IN (
             }
             : qq{
@@ -343,6 +322,12 @@ sub SendAlerts {
             LEFT JOIN aqbooksellers ON subscription.aqbooksellerid=aqbooksellers.id
             WHERE serial.serialid IN (
             };
+
+        if (!@$externalid){
+            carp "No Order seleted";
+            return { error => "no_order_seleted" };
+        }
+
         $strsth .= join( ",", @$externalid ) . ")";
         my $sthorders = $dbh->prepare($strsth);
         $sthorders->execute;
@@ -352,14 +337,24 @@ sub SendAlerts {
           $dbh->prepare("select * from aqbooksellers where id=?");
         $sthbookseller->execute( $dataorders->[0]->{booksellerid} );
         my $databookseller = $sthbookseller->fetchrow_hashref;
+        my $addressee =  $type eq 'claimacquisition' ? 'acqprimary' : 'serialsprimary';
+        my $sthcontact =
+          $dbh->prepare("SELECT * FROM aqcontacts WHERE booksellerid=? AND $type=1 ORDER BY $addressee DESC");
+        $sthcontact->execute( $dataorders->[0]->{booksellerid} );
+        my $datacontact = $sthcontact->fetchrow_hashref;
 
         my @email;
+        my @cc;
         push @email, $databookseller->{bookselleremail} if $databookseller->{bookselleremail};
-        push @email, $databookseller->{contemail}       if $databookseller->{contemail};
+        push @email, $datacontact->{email}           if ( $datacontact && $datacontact->{email} );
         unless (@email) {
             warn "Bookseller $dataorders->[0]->{booksellerid} without emails";
             return { error => "no_email" };
         }
+        my $addlcontact;
+        while ($addlcontact = $sthcontact->fetchrow_hashref) {
+            push @cc, $addlcontact->{email} if ( $addlcontact && $addlcontact->{email} );
+        }
 
         my $userenv = C4::Context->userenv;
         my $letter = GetPreparedLetter (
@@ -369,6 +364,7 @@ sub SendAlerts {
             tables => {
                 'branches'    => $userenv->{branch},
                 'aqbooksellers' => $databookseller,
+                'aqcontacts'    => $datacontact,
             },
             repeat => $dataorders,
             want_librarian => 1,
@@ -377,19 +373,29 @@ sub SendAlerts {
         # ... then send mail
         my %mail = (
             To => join( ',', @email),
+            Cc             => join( ',', @cc),
             From           => $userenv->{emailaddress},
             Subject        => Encode::encode( "utf8", "" . $letter->{title} ),
             Message        => Encode::encode( "utf8", "" . $letter->{content} ),
             'Content-Type' => 'text/plain; charset="utf8"',
         );
-        sendmail(%mail) or carp $Mail::Sendmail::error;
+
+        $mail{'Reply-to'} = C4::Context->preference('ReplytoDefault')
+          if C4::Context->preference('ReplytoDefault');
+        $mail{'Sender'} = C4::Context->preference('ReturnpathDefault')
+          if C4::Context->preference('ReturnpathDefault');
+
+        unless ( sendmail(%mail) ) {
+            carp $Mail::Sendmail::error;
+            return { error => $Mail::Sendmail::error };
+        }
 
         logaction(
             "ACQUISITION",
             $type eq 'claimissues' ? "CLAIM ISSUE" : "ACQUISITION CLAIM",
             undef,
             "To="
-                . $databookseller->{contemail}
+                . join( ',', @email )
                 . " Title="
                 . $letter->{title}
                 . " Content="
@@ -410,14 +416,18 @@ sub SendAlerts {
             substitute => { 'borrowers.password' => $externalid->{'password'} },
             want_librarian => 1,
         ) or return;
-
         return { error => "no_email" } unless $externalid->{'emailaddr'};
-        my %mail = (
-                To      =>     $externalid->{'emailaddr'},
-                From    =>  $branchdetails->{'branchemail'} || C4::Context->preference("KohaAdminEmailAddress"),
-                Subject => Encode::encode( "utf8", $letter->{'title'} ),
-                Message => Encode::encode( "utf8", $letter->{'content'} ),
-                'Content-Type' => 'text/plain; charset="utf8"',
+        my $email = Koha::Email->new();
+        my %mail  = $email->create_message_headers(
+            {
+                to      => $externalid->{'emailaddr'},
+                from    => $branchdetails->{'branchemail'},
+                replyto => $branchdetails->{'branchreplyto'},
+                sender  => $branchdetails->{'branchreturnpath'},
+                subject => Encode::encode( "utf8", "" . $letter->{'title'} ),
+                message => Encode::encode( "utf8", "" . $letter->{'content'} ),
+                contenttype => 'text/plain; charset="utf8"'
+            }
         );
         sendmail(%mail) or carp $Mail::Sendmail::error;
     }
@@ -452,9 +462,10 @@ sub GetPreparedLetter {
     my $module      = $params{module} or croak "No module";
     my $letter_code = $params{letter_code} or croak "No letter_code";
     my $branchcode  = $params{branchcode} || '';
+    my $mtt         = $params{message_transport_type} || 'email';
 
-    my $letter = getletter( $module, $letter_code, $branchcode, $params{message_transport_type} )
-        or warn( "No $module $letter_code letter"),
+    my $letter = getletter( $module, $letter_code, $branchcode, $mtt )
+        or warn( "No $module $letter_code letter transported by " . $mtt ),
             return;
 
     my $tables = $params{tables};
@@ -548,7 +559,6 @@ sub _substitute_tables {
             $values = $param;
         }
         else {
-            my @pk;
             my $sth = _parseletter_sth($table);
             unless ($sth) {
                 warn "_parseletter_sth('$table') failed to return a valid sth.  No substitution will be done for that table.";
@@ -588,7 +598,7 @@ sub _parseletter_sth {
     ($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 =?":
+    ($table eq 'borrower_modifications') ? "SELECT * FROM $table WHERE verification_token = ?" :
     undef ;
     unless ($query) {
         warn "ERROR: No _parseletter_sth query for table '$table'";
@@ -645,6 +655,18 @@ sub _parseletter {
 
         $val = GetAuthorisedValueByCode ('ROADTYPE', $val, 0) if $table=~/^borrowers$/ && $field=~/^streettype$/;
         my $replacedby   = defined ($val) ? $val : '';
+        if (    $replacedby
+            and not $replacedby =~ m|0000-00-00|
+            and not $replacedby =~ m|9999-12-31|
+            and $replacedby =~ m|^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}:\d{2})?$| )
+        {
+            # If the value is XXXX-YY-ZZ[ AA:BB:CC] we assume it is a date
+            my $dateonly = defined $1 ? 0 : 1; #$1 refers to the capture group wrapped in parentheses. In this case, that's the hours, minutes, seconds.
+            eval {
+                $replacedby = output_pref({ dt => dt_from_string( $replacedby ), dateonly => $dateonly });
+            };
+            warn "$replacedby seems to be a date but an error occurs on generating it ($@)" if $@;
+        }
         ($letter->{title}  ) and do {
             $letter->{title}   =~ s/$replacetablefield/$replacedby/g;
             $letter->{title}   =~ s/$replacefield/$replacedby/g;
@@ -916,7 +938,7 @@ sub _get_unsent_messages {
 
     my $dbh = C4::Context->dbh();
     my $statement = << 'ENDSQL';
-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
+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, mq.letter_code
   FROM message_queue mq
   LEFT JOIN borrowers b ON b.borrowernumber = mq.borrowernumber
  WHERE status = ?
@@ -974,17 +996,28 @@ sub _send_message_by_email {
     my $content = encode('utf8', $message->{'content'});
     my $content_type = $message->{'content_type'} || 'text/plain; charset="UTF-8"';
     my $is_html = $content_type =~ m/html/io;
-
-    my $branch_email = ( $member ) ? GetBranchDetail( $member->{'branchcode'} )->{'branchemail'} : undef;
-
-    my %sendmail_params = (
-        To   => $to_address,
-        From => $message->{'from_address'} || $branch_email || C4::Context->preference('KohaAdminEmailAddress'),
-        Subject => $subject,
-        charset => 'utf8',
-        Message => $is_html ? _wrap_html($content, $subject) : $content,
-        'content-type' => $content_type,
+    my $branch_email = undef;
+    my $branch_replyto = undef;
+    my $branch_returnpath = undef;
+    if ($member){
+        my $branchdetail = GetBranchDetail( $member->{'branchcode'} );
+        $branch_email = $branchdetail->{'branchemail'};
+        $branch_replyto = $branchdetail->{'branchreplyto'};
+        $branch_returnpath = $branchdetail->{'branchreturnpath'};
+    }
+    my $email = Koha::Email->new();
+    my %sendmail_params = $email->create_message_headers(
+        {
+            to      => $to_address,
+            from    => $message->{'from_address'} || $branch_email,
+            replyto => $branch_replyto,
+            sender  => $branch_returnpath,
+            subject => $subject,
+            message => $is_html ? _wrap_html( $content, $subject ) : $content,
+            contenttype => $content_type
+        }
     );
+
     $sendmail_params{'Auth'} = {user => $username, pass => $password, method => $method} if $username;
     if ( my $bcc = C4::Context->preference('OverdueNoticeBcc') ) {
        $sendmail_params{ Bcc } = $bcc;
@@ -1024,6 +1057,22 @@ $content
 EOS
 }
 
+sub _is_duplicate {
+    my ( $message ) = @_;
+    my $dbh = C4::Context->dbh;
+    my $count = $dbh->selectrow_array(q|
+        SELECT COUNT(*)
+        FROM message_queue
+        WHERE message_transport_type = ?
+        AND borrowernumber = ?
+        AND letter_code = ?
+        AND CAST(time_queued AS date) = CAST(NOW() AS date)
+        AND status="sent"
+        AND content = ?
+    |, {}, $message->{message_transport_type}, $message->{borrowernumber}, $message->{letter_code}, $message->{content} );
+    return $count;
+}
+
 sub _send_message_by_sms {
     my $message = shift or return;
     my $member = C4::Members::GetMember( 'borrowernumber' => $message->{'borrowernumber'} );
@@ -1034,6 +1083,12 @@ sub _send_message_by_sms {
         return;
     }
 
+    if ( _is_duplicate( $message ) ) {
+        _set_message_status( { message_id => $message->{'message_id'},
+                               status     => 'failed' } );
+        return;
+    }
+
     my $success = C4::SMS->send_sms( { destination => $member->{'smsalertnumber'},
                                        message     => $message->{'content'},
                                      } );