Bug 17600: Standardize our EXPORT_OK
[srvgit] / misc / cronjobs / thirdparty / TalkingTech_itiva_outbound.pl
index b4d4e9c..fedc3cf 100755 (executable)
@@ -4,18 +4,18 @@
 #
 # This file is part of Koha.
 #
-# Koha is free software; you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 2 of the License, or (at your option) any later
-# version.
+# Koha is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
 #
-# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+# Koha is distributed in the hope that it will be useful, but
+# WITHOUT ANY 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.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see <http://www.gnu.org/licenses>.
 
 use strict;
 use warnings;
@@ -24,28 +24,28 @@ 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 Date::Calc qw(Add_Delta_Days);
+use Getopt::Long qw( GetOptions );
+use Pod::Usage qw( pod2usage );
 
+use Koha::Script -cron;
 use C4::Context;
-use C4::Items;
 use C4::Letters;
 use C4::Overdues;
-use C4::Dates;
-use C4::Calendar;
+use Koha::Calendar;
+use Koha::DateUtils qw( dt_from_string output_pref );
+use Koha::Patrons;
+use Koha::Libraries;
 
 sub usage {
     pod2usage( -verbose => 2 );
     exit;
 }
 
-die
-  "TalkingTechItivaPhoneNotification system preference not activated... dying\n"
+die "TalkingTechItivaPhoneNotification system preference not activated... dying\n"
   unless ( C4::Context->preference("TalkingTechItivaPhoneNotification") );
 
 # Database handle
@@ -59,6 +59,8 @@ my @holds_waiting_days_to_call;
 my $library_code;
 my $help;
 my $outfile;
+my $skip_patrons_with_email;
+my $patron_branchcode;
 
 # maps to convert I-tiva terms to Koha terms
 my $type_module_map = {
@@ -68,19 +70,21 @@ my $type_module_map = {
 };
 
 my $type_notice_map = {
-    'PREOVERDUE' => 'PREDUE_PHONE',
-    'OVERDUE'    => 'OVERDUE_PHONE',
-    'RESERVE'    => 'HOLD_PHONE',
+    'PREOVERDUE' => 'PREDUE',
+    'OVERDUE'    => 'OVERDUE',
+    'RESERVE'    => 'HOLD',
 };
 
 GetOptions(
-    'o|output:s'            => \$outfile,
-    'v'                     => \$verbose,
-    'lang:s'                => \$language,
-    'type:s'                => \@types,
-    'w|waiting-hold-day:s'  => \@holds_waiting_days_to_call,
-    'c|code|library-code:s' => \$library_code,
-    'help|h'                => \$help,
+    'o|output:s'             => \$outfile,
+    'v'                      => \$verbose,
+    'lang:s'                 => \$language,
+    'type:s'                 => \@types,
+    'w|waiting-hold-day:s'   => \@holds_waiting_days_to_call,
+    'c|code|library-code:s'  => \$library_code,
+    's|skip-patrons-with-email' => \$skip_patrons_with_email,
+    'pb|patron-branchcode:s' => \$patron_branchcode,
+    'h|help'                 => \$help,
 );
 
 $language = uc($language);
@@ -88,53 +92,59 @@ $library_code ||= '';
 
 pod2usage( -verbose => 1 ) if $help;
 
+if ($patron_branchcode) {
+    die("Invalid branchcode '$patron_branchcode' passed in -pb --patron-branchcode parameter")
+      unless Koha::Libraries->search( { branchcode => $patron_branchcode } )->count;
+}
+
 # output log or STDOUT
 my $OUT;
 if ( defined $outfile ) {
     open( $OUT, '>', "$outfile" ) || die("Cannot open output file");
-}
-else {
+} else {
     print "No output file defined; printing to STDOUT\n"
       if ( defined $verbose );
-    open( $OUT, '>', "&STDOUT" ) || die("Couldn't duplicate STDOUT: $!");
+    $OUT = *STDOUT || die "Couldn't duplicate STDOUT: $!";
 }
 
 my $format = 'V';    # format for phone notifications
 
 foreach my $type (@types) {
     $type = uc($type);    #just in case lower or mixed-case was supplied
-    my $module =
-      $type_module_map->{$type}; #since the module is required to get the letter
-    my $code = $type_notice_map->{$type};    #to get the Koha name of the notice
+    my $module = $type_module_map->{$type};    #since the module is required to get the letter
+    my $code   = $type_notice_map->{$type};    #to get the Koha name of the notice
 
     my @loop;
     if ( $type eq 'OVERDUE' ) {
-        @loop = GetOverdueIssues();
-    }
-    elsif ( $type eq 'PREOVERDUE' ) {
-        @loop = GetPredueIssues();
-    }
-    elsif ( $type eq 'RESERVE' ) {
-        @loop = GetWaitingHolds();
-    }
-    else {
+        @loop = GetOverdueIssues( $patron_branchcode );
+    } elsif ( $type eq 'PREOVERDUE' ) {
+        @loop = GetPredueIssues( $patron_branchcode );
+    } elsif ( $type eq 'RESERVE' ) {
+        @loop = GetWaitingHolds( $patron_branchcode );
+    } else {
         print "Unknown or unsupported message type $type; skipping...\n"
           if ( defined $verbose );
         next;
     }
 
+    my $patrons;
     foreach my $issues (@loop) {
-        my $date = C4::Dates->new( $issues->{'date_due'}, 'iso' );
-        my $due_date = $date->output('metric');
+        $patrons->{$issues->{borrowernumber}} ||= Koha::Patrons->find( $issues->{borrowernumber} ) if $skip_patrons_with_email;
+        next if $skip_patrons_with_email && $patrons->{$issues->{borrowernumber}}->notice_email_address;
+
+        my $date_dt = dt_from_string ( $issues->{'date_due'} );
+        my $due_date = output_pref( { dt => $date_dt, dateonly => 1, dateformat =>'metric' } );
 
         my $letter = C4::Letters::GetPreparedLetter(
             module      => $module,
             letter_code => $code,
+            lang        => 'default', # It does not sound useful to send a lang here
             tables      => {
                 borrowers   => $issues->{'borrowernumber'},
                 biblio      => $issues->{'biblionumber'},
-                biblioitems => $issues->{'biblionumber'}
+                biblioitems => $issues->{'biblionumber'},
             },
+            message_transport_type => 'itiva',
         );
 
         die "No letter found for type $type!... dying\n" unless $letter;
@@ -142,20 +152,19 @@ foreach my $type (@types) {
         my $message_id = 0;
         if ($outfile) {
             $message_id = C4::Letters::EnqueueLetter(
-                {
-                    letter                 => $letter,
+                {   letter                 => $letter,
                     borrowernumber         => $issues->{'borrowernumber'},
-                    message_transport_type => 'phone',
+                    message_transport_type => 'itiva',
                 }
             );
         }
 
-        print $OUT
-"\"$format\",\"$language\",\"$type\",\"$issues->{level}\",\"$issues->{cardnumber}\",\"$issues->{patron_title}\",\"$issues->{firstname}\",";
-        print $OUT
-"\"$issues->{surname}\",\"$issues->{phone}\",\"$issues->{email}\",\"$library_code\",";
-        print $OUT
-"\"$issues->{site}\",\"$issues->{site_name}\",\"$issues->{barcode}\",\"$due_date\",\"$issues->{title}\",\"$message_id\"\n";
+        $issues->{title} =~ s/'//g;
+        $issues->{title} =~ s/"//g;
+
+        print $OUT "\"$format\",\"$language\",\"$type\",\"$issues->{level}\",\"$issues->{cardnumber}\",\"$issues->{patron_title}\",\"$issues->{firstname}\",";
+        print $OUT "\"$issues->{surname}\",\"$issues->{phone}\",\"$issues->{email}\",\"$library_code\",";
+        print $OUT "\"$issues->{site}\",\"$issues->{site_name}\",\"$issues->{barcode}\",\"$due_date\",\"$issues->{title}\",\"$message_id\"\n";
     }
 }
 
@@ -173,6 +182,8 @@ TalkingTech_itiva_outbound.pl
 Script to generate Spec C outbound notifications file for Talking Tech i-tiva
 phone notification system.
 
+=over
+
 =item B<--help> B<-h>
 
 Prints this help
@@ -214,11 +225,23 @@ consortium purposes and apply library specific settings, such as
 prompts, to those notices.
 This field can be blank if all messages are from a single library.
 
+=item B<--patron-branchcode> B<--pb>
+
+OPTIONAL
+
+Limits the the patrons to generate notices for based on the patron's home library.
+Items and holds from other libraries will still be included for the given patron.
+
+=back
+
 =cut
 
 sub GetOverdueIssues {
-    my $query =
-"SELECT borrowers.borrowernumber, borrowers.cardnumber, borrowers.title as patron_title, borrowers.firstname, borrowers.surname,
+    my ( $patron_branchcode ) = @_;
+
+    my $patron_branchcode_filter = $patron_branchcode ? "AND borrowers.branchcode = '$patron_branchcode'" : q{};
+
+    my $query = "SELECT borrowers.borrowernumber, borrowers.cardnumber, borrowers.title as patron_title, borrowers.firstname, borrowers.surname,
                 borrowers.phone, borrowers.email, borrowers.branchcode, biblio.biblionumber, biblio.title, items.barcode, issues.date_due,
                 max(overduerules.branchcode) as rulebranch, TO_DAYS(NOW())-TO_DAYS(date_due) as daysoverdue, delay1, delay2, delay3,
                 issues.branchcode as site, branches.branchname as site_name
@@ -227,10 +250,13 @@ sub GetOverdueIssues {
                 JOIN biblio USING (biblionumber)
                 JOIN branches ON (issues.branchcode = branches.branchcode)
                 JOIN overduerules USING (categorycode)
+                JOIN overduerules_transport_types USING ( overduerules_id )
                 WHERE ( overduerules.branchcode = borrowers.branchcode or overduerules.branchcode = '')
+                AND overduerules_transport_types.message_transport_type = 'itiva'
                 AND ( (TO_DAYS(NOW())-TO_DAYS(date_due) ) = delay1
                   OR  (TO_DAYS(NOW())-TO_DAYS(date_due) ) = delay2
                   OR  (TO_DAYS(NOW())-TO_DAYS(date_due) ) = delay3 )
+                $patron_branchcode_filter
                 GROUP BY items.itemnumber
                 ";
     my $sth = $dbh->prepare($query);
@@ -239,14 +265,11 @@ sub GetOverdueIssues {
     while ( my $issue = $sth->fetchrow_hashref() ) {
         if ( $issue->{'daysoverdue'} == $issue->{'delay1'} ) {
             $issue->{'level'} = 1;
-        }
-        elsif ( $issue->{'daysoverdue'} == $issue->{'delay2'} ) {
+        } elsif ( $issue->{'daysoverdue'} == $issue->{'delay2'} ) {
             $issue->{'level'} = 2;
-        }
-        elsif ( $issue->{'daysoverdue'} == $issue->{'delay3'} ) {
+        } elsif ( $issue->{'daysoverdue'} == $issue->{'delay3'} ) {
             $issue->{'level'} = 3;
-        }
-        else {
+        } else {
 
             # this shouldn't ever happen, based our SQL criteria
         }
@@ -256,8 +279,11 @@ sub GetOverdueIssues {
 }
 
 sub GetPredueIssues {
-    my $query =
-"SELECT borrowers.borrowernumber, borrowers.cardnumber, borrowers.title as patron_title, borrowers.firstname, borrowers.surname,
+    my ( $patron_branchcode ) = @_;
+
+    my $patron_branchcode_filter = $patron_branchcode ? "AND borrowers.branchcode = '$patron_branchcode'" : q{};
+
+    my $query = "SELECT borrowers.borrowernumber, borrowers.cardnumber, borrowers.title as patron_title, borrowers.firstname, borrowers.surname,
                 borrowers.phone, borrowers.email, borrowers.branchcode, biblio.biblionumber, biblio.title, items.barcode, issues.date_due,
                 issues.branchcode as site, branches.branchname as site_name
                 FROM borrowers JOIN issues USING (borrowernumber)
@@ -268,8 +294,9 @@ sub GetPredueIssues {
                 JOIN borrower_message_transport_preferences USING (borrower_message_preference_id)
                 JOIN message_attributes USING (message_attribute_id)
                 WHERE ( TO_DAYS( date_due ) - TO_DAYS( NOW() ) ) = days_in_advance
-                AND message_transport_type = 'phone'
+                AND message_transport_type = 'itiva'
                 AND message_name = 'Advance_Notice'
+                $patron_branchcode_filter
                 ";
     my $sth = $dbh->prepare($query);
     $sth->execute();
@@ -282,8 +309,11 @@ sub GetPredueIssues {
 }
 
 sub GetWaitingHolds {
-    my $query =
-"SELECT borrowers.borrowernumber, borrowers.cardnumber, borrowers.title as patron_title, borrowers.firstname, borrowers.surname,
+    my ( $patron_branchcode ) = @_;
+
+    my $patron_branchcode_filter = $patron_branchcode ? "AND borrowers.branchcode = '$patron_branchcode'" : q{};
+
+    my $query = "SELECT borrowers.borrowernumber, borrowers.cardnumber, borrowers.title as patron_title, borrowers.firstname, borrowers.surname, borrowers.categorycode,
                 borrowers.phone, borrowers.email, borrowers.branchcode, biblio.biblionumber, biblio.title, items.barcode, reserves.waitingdate,
                 reserves.branchcode AS site, branches.branchname AS site_name,
                 TO_DAYS(NOW())-TO_DAYS(reserves.waitingdate) AS days_since_waiting
@@ -295,48 +325,45 @@ sub GetWaitingHolds {
                 JOIN borrower_message_transport_preferences USING (borrower_message_preference_id)
                 JOIN message_attributes USING (message_attribute_id)
                 WHERE ( reserves.found = 'W' )
-                AND message_transport_type = 'phone'
+                AND message_transport_type = 'itiva'
                 AND message_name = 'Hold_Filled'
+                $patron_branchcode_filter
                 ";
     my $pickupdelay = C4::Context->preference("ReservesMaxPickUpDelay");
     my $sth         = $dbh->prepare($query);
     $sth->execute();
     my @results;
     while ( my $issue = $sth->fetchrow_hashref() ) {
-        my @waitingdate = split( /-/, $issue->{'waitingdate'} );
-        my @date_due =
-          Add_Delta_Days( $waitingdate[0], $waitingdate[1], $waitingdate[2],
-            $pickupdelay );
-        $issue->{'date_due'} =
-          sprintf( "%04d-%02d-%02d", $date_due[0], $date_due[1], $date_due[2] );
-        $issue->{'level'} = 1;   # only one level for Hold Waiting notifications
+        my $item = Koha::Items->find({ barcode => $issue->{barcode} });
+        my $daysmode = Koha::CirculationRules->get_effective_daysmode(
+            {
+                categorycode => $issue->{categorycode},
+                itemtype     => $item->effective_itemtype,
+                branchcode   => $issue->{site},
+            }
+        );
+
+        my $calendar = Koha::Calendar->new( branchcode => $issue->{'site'}, days_mode => $daysmode );
+
+        my $waiting_date = dt_from_string( $issue->{waitingdate}, 'sql' );
+        my $pickup_date = $waiting_date->clone->add( days => $pickupdelay );
+        if ( $calendar->is_holiday($pickup_date) ) {
+            $pickup_date = $calendar->next_open_days( $pickup_date, 1 );
+        }
+
+        $issue->{'date_due'} = output_pref({dt => $pickup_date, dateformat => 'iso' });
+        $issue->{'level'} = 1;    # only one level for Hold Waiting notifications
 
         my $days_to_subtract = 0;
-        my $calendar = C4::Calendar->new( branchcode => $issue->{'site'} );
-        while (
-            $calendar->isHoliday(
-                reverse(
-                    Add_Delta_Days(
-                        $waitingdate[0], $waitingdate[1],
-                        $waitingdate[2], $days_to_subtract
-                    )
-                )
-            )
-          )
-        {
-            $days_to_subtract++;
+        if ( $calendar->is_holiday($waiting_date) ) {
+            my $next_open_day = $calendar->next_open_days( $waiting_date, 1 );
+            $days_to_subtract = $calendar->days_between($waiting_date, $next_open_day)->days;
         }
-        $issue->{'days_since_waiting'} =
-          $issue->{'days_since_waiting'} - $days_to_subtract;
-
-        if (
-            (
-                grep $_ eq $issue->{'days_since_waiting'},
-                @holds_waiting_days_to_call
-            )
-            || !scalar(@holds_waiting_days_to_call)
-          )
-        {
+
+        $issue->{'days_since_waiting'} = $issue->{'days_since_waiting'} - $days_to_subtract;
+
+        if ( ( grep $_ eq $issue->{'days_since_waiting'}, @holds_waiting_days_to_call )
+            || !scalar(@holds_waiting_days_to_call) ) {
             push @results, $issue;
         }
     }