| Line 0
          
      
      
        Link Here | 
            
              |  |  | 1 | #!/usr/bin/perl | 
            
              | 2 | # | 
            
              | 3 | # Copyright (C) 2011 ByWater Solutions | 
            
              | 4 | # | 
            
              | 5 | # This file is part of Koha. | 
            
              | 6 | # | 
            
              | 7 | # Koha is free software; you can redistribute it and/or modify it under the | 
            
              | 8 | # terms of the GNU General Public License as published by the Free Software | 
            
              | 9 | # Foundation; either version 2 of the License, or (at your option) any later | 
            
              | 10 | # version. | 
            
              | 11 | # | 
            
              | 12 | # Koha is distributed in the hope that it will be useful, but WITHOUT ANY | 
            
              | 13 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | 
            
              | 14 | # A PARTICULAR PURPOSE.  See the GNU General Public License for more details. | 
            
              | 15 | # | 
            
              | 16 | # You should have received a copy of the GNU General Public License along | 
            
              | 17 | # with Koha; if not, write to the Free Software Foundation, Inc., | 
            
              | 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 
            
              | 19 |  | 
            
              | 20 | use strict; | 
            
              | 21 | use warnings; | 
            
              | 22 | BEGIN { | 
            
              | 23 |     # find Koha's Perl modules | 
            
              | 24 |     # test carefully before changing this | 
            
              | 25 |     use FindBin; | 
            
              | 26 |     eval { require "$FindBin::Bin/../kohalib.pl" }; | 
            
              | 27 | } | 
            
              | 28 |  | 
            
              | 29 | use Getopt::Long; | 
            
              | 30 | use Pod::Usage; | 
            
              | 31 | use Date::Calc qw(Add_Delta_Days); | 
            
              | 32 |  | 
            
              | 33 | use C4::Context; | 
            
              | 34 | use C4::Items; | 
            
              | 35 | use C4::Letters; | 
            
              | 36 | use C4::Overdues; | 
            
              | 37 | use C4::Dates; | 
            
              | 38 | use C4::Calendar; | 
            
              | 39 |  | 
            
              | 40 | sub usage { | 
            
              | 41 |     pod2usage( -verbose => 2 ); | 
            
              | 42 |     exit; | 
            
              | 43 | } | 
            
              | 44 |  | 
            
              | 45 | die "TalkingTechItivaPhoneNotification system preference not activated... dying\n" unless (C4::Context->preference("TalkingTechItivaPhoneNotification")); | 
            
              | 46 |  | 
            
              | 47 | # Database handle | 
            
              | 48 | my $dbh = C4::Context->dbh; | 
            
              | 49 |  | 
            
              | 50 | # Options | 
            
              | 51 | my $verbose; | 
            
              | 52 | my $language = "EN"; | 
            
              | 53 | my @types; | 
            
              | 54 | my @holds_waiting_days_to_call; | 
            
              | 55 | my $library_code; | 
            
              | 56 | my $help; | 
            
              | 57 | my $outfile; | 
            
              | 58 |  | 
            
              | 59 | # maps to convert I-tiva terms to Koha terms | 
            
              | 60 | my $type_module_map = { 'PREOVERDUE'  => 'circulation', | 
            
              | 61 |                         'OVERDUE' => 'circulation', | 
            
              | 62 |                         'RESERVE'    => 'reserves', | 
            
              | 63 |                       }; | 
            
              | 64 |  | 
            
              | 65 | my $type_notice_map = { 'PREOVERDUE'  => 'PREDUE_PHONE', | 
            
              | 66 |                         'OVERDUE' => 'OVERDUE_PHONE', | 
            
              | 67 |                         'RESERVE'    => 'HOLD_PHONE', | 
            
              | 68 |                       }; | 
            
              | 69 |  | 
            
              | 70 | GetOptions( | 
            
              | 71 |   'o|output:s' => \$outfile, | 
            
              | 72 |   'v' => \$verbose, | 
            
              | 73 |   'lang:s' => \$language, | 
            
              | 74 |   'type:s' => \@types, | 
            
              | 75 |   'w|waiting-hold-day:s' => \@holds_waiting_days_to_call, | 
            
              | 76 |   'c|code|library-code:s' => \$library_code, | 
            
              | 77 |   'help|h'   => \$help, | 
            
              | 78 | ); | 
            
              | 79 |  | 
            
              | 80 | $language = uc($language); | 
            
              | 81 | $library_code ||= ''; | 
            
              | 82 |  | 
            
              | 83 | pod2usage( -verbose => 1 ) if $help; | 
            
              | 84 |  | 
            
              | 85 | # output log or STDOUT | 
            
              | 86 | if (defined $outfile) { | 
            
              | 87 |    open (OUT, ">$outfile") || die ("Cannot open output file"); | 
            
              | 88 | } else { | 
            
              | 89 |    print "No output file defined; printing to STDOUT\n" if (defined $verbose); | 
            
              | 90 |    open(OUT, ">&STDOUT") || die ("Couldn't duplicate STDOUT: $!"); | 
            
              | 91 | } | 
            
              | 92 |  | 
            
              | 93 | my $format = 'V'; # format for phone notifications | 
            
              | 94 |  | 
            
              | 95 | foreach my $type (@types) { | 
            
              | 96 |    $type = uc($type); #just in case lower or mixed-case was supplied | 
            
              | 97 |    my $module = $type_module_map->{$type}; #since the module is required to get the letter | 
            
              | 98 |    my $code = $type_notice_map->{$type}; #to get the Koha name of the notice | 
            
              | 99 |  | 
            
              | 100 |    my @loop; | 
            
              | 101 |    if ($type eq 'OVERDUE') { | 
            
              | 102 |      @loop = GetOverdueIssues(); | 
            
              | 103 |    } elsif ($type eq 'PREOVERDUE') { | 
            
              | 104 |      @loop = GetPredueIssues(); | 
            
              | 105 |    } elsif ($type eq 'RESERVE') { | 
            
              | 106 |      @loop = GetWaitingHolds(); | 
            
              | 107 |    } else { | 
            
              | 108 |       print "Unknown or unsupported message type $type; skipping...\n" if (defined $verbose); | 
            
              | 109 |       next; | 
            
              | 110 |    } | 
            
              | 111 |  | 
            
              | 112 |    foreach my $issues (@loop) { | 
            
              | 113 |       my $date = C4::Dates->new($issues->{'date_due'}, 'iso'); | 
            
              | 114 |       my $due_date = $date->output('metric'); | 
            
              | 115 |  | 
            
              | 116 |       # gets the placeholder message, and enqueues the letter | 
            
              | 117 |       my $letter = getletter($module, $code); | 
            
              | 118 |       die "No letter found for type $type!... dying\n" unless $letter; | 
            
              | 119 |       # covers basic variable parsing in letter | 
            
              | 120 |       $letter = C4::Letters::parseletter($letter, 'borrowers', $issues->{'borrowernumber'}); | 
            
              | 121 |       $letter = C4::Letters::parseletter($letter, 'biblio', $issues->{'biblionumber'}); | 
            
              | 122 |       $letter = C4::Letters::parseletter($letter, 'biblioitems', $issues->{'biblionumber'}); | 
            
              | 123 |  | 
            
              | 124 |       my $message_id = 0; | 
            
              | 125 |       if ($outfile) { | 
            
              | 126 |          $message_id = C4::Letters::EnqueueLetter( {  letter => $letter, | 
            
              | 127 |                                                       borrowernumber => $issues->{'borrowernumber'}, | 
            
              | 128 |                                                       message_transport_type => 'phone', | 
            
              | 129 |                                                    }); | 
            
              | 130 |       } | 
            
              | 131 |           | 
            
              | 132 |       print OUT "\"$format\",\"$language\",\"$type\",\"$issues->{level}\",\"$issues->{cardnumber}\",\"$issues->{patron_title}\",\"$issues->{firstname}\","; | 
            
              | 133 |       print OUT "\"$issues->{surname}\",\"$issues->{phone}\",\"$issues->{email}\",\"$library_code\","; | 
            
              | 134 |       print OUT "\"$issues->{site}\",\"$issues->{site_name}\",\"$issues->{barcode}\",\"$due_date\",\"$issues->{title}\",\"$message_id\"\n"; | 
            
              | 135 |    } | 
            
              | 136 | } | 
            
              | 137 |  | 
            
              | 138 |  | 
            
              | 139 | =head1 NAME | 
            
              | 140 |  | 
            
              | 141 | TalkingTech_itiva_outbound.pl | 
            
              | 142 |  | 
            
              | 143 | =head1 SYNOPSIS | 
            
              | 144 |  | 
            
              | 145 |   TalkingTech_itiva_outbound.pl | 
            
              | 146 |   TalkingTech_itiva_outbound.pl --type=OVERDUE -w 0 -w 2 -w 6 --output=/tmp/talkingtech/outbound.csv | 
            
              | 147 |   TalkingTech_itiva_outbound.pl --type=RESERVE --type=PREOVERDUE --lang=FR | 
            
              | 148 |  | 
            
              | 149 |  | 
            
              | 150 | Script to generate Spec C outbound notifications file for Talking Tech i-tiva | 
            
              | 151 | phone notification system. | 
            
              | 152 |  | 
            
              | 153 | =item B<--help> B<-h> | 
            
              | 154 |  | 
            
              | 155 | Prints this help | 
            
              | 156 |  | 
            
              | 157 | =item B<-v> | 
            
              | 158 |  | 
            
              | 159 | Provide verbose log information. | 
            
              | 160 |  | 
            
              | 161 | =item B<--output> B<-o> | 
            
              | 162 |  | 
            
              | 163 | Destination for outbound notifications file (CSV format).  If no value is specified, | 
            
              | 164 | output is dumped to screen. | 
            
              | 165 |  | 
            
              | 166 | =item B<--lang> | 
            
              | 167 |  | 
            
              | 168 | Sets the language for all outbound messages.  Currently supported values are EN, FR and ES. | 
            
              | 169 | If no value is specified, EN will be used by default. | 
            
              | 170 |  | 
            
              | 171 | =item B<--type> | 
            
              | 172 |  | 
            
              | 173 | REQUIRED. Sets which messaging types are to be used.  Can be given multiple times, to  | 
            
              | 174 | specify multiple types in a single output file.  Currently supported values are RESERVE, PREOVERDUE | 
            
              | 175 | and OVERDUE.  If no value is given, this script will not produce any outbound notifications. | 
            
              | 176 |  | 
            
              | 177 | =item B<--waiting-hold-day> B<-w> | 
            
              | 178 |  | 
            
              | 179 | OPTIONAL for --type=RESERVE. Sets the days after a hold has been set to waiting on which to call. Use | 
            
              | 180 | switch as many times as desired. For example, passing "-w 0 -w 2 -w 6" will cause calls to be placed | 
            
              | 181 | on the day the hold was set to waiting, 2 days after the waiting date, and 6 days after. See example above. | 
            
              | 182 | If this switch is not used with --type=RESERVE, calls will be placed every day until the waiting reserve | 
            
              | 183 | is picked up or canceled. | 
            
              | 184 |  | 
            
              | 185 | =item B<--library-code> B<--code> B<-c> | 
            
              | 186 |  | 
            
              | 187 | OPTIONAL | 
            
              | 188 | The code of the source library of the message.  | 
            
              | 189 | The library code is used to group notices together for  | 
            
              | 190 | consortium purposes and apply library specific settings, such as  | 
            
              | 191 | prompts, to those notices.  | 
            
              | 192 | This field can be blank if all messages are from a single library.  | 
            
              | 193 |  | 
            
              | 194 | =cut | 
            
              | 195 |  | 
            
              | 196 | sub GetOverdueIssues { | 
            
              | 197 |    my $query = "SELECT borrowers.borrowernumber, borrowers.cardnumber, borrowers.title as patron_title, borrowers.firstname, borrowers.surname, | 
            
              | 198 |                 borrowers.phone, borrowers.email, borrowers.branchcode, biblio.biblionumber, biblio.title, items.barcode, issues.date_due, | 
            
              | 199 |                 max(overduerules.branchcode) as rulebranch, TO_DAYS(NOW())-TO_DAYS(date_due) as daysoverdue, delay1, delay2, delay3, | 
            
              | 200 |                 issues.branchcode as site, branches.branchname as site_name | 
            
              | 201 |                 FROM borrowers JOIN issues USING (borrowernumber) | 
            
              | 202 |                 JOIN items USING (itemnumber) | 
            
              | 203 |                 JOIN biblio USING (biblionumber) | 
            
              | 204 |                 JOIN branches ON (issues.branchcode = branches.branchcode) | 
            
              | 205 |                 JOIN overduerules USING (categorycode) | 
            
              | 206 |                 WHERE ( overduerules.branchcode = borrowers.branchcode or overduerules.branchcode = '') | 
            
              | 207 |                 AND ( (TO_DAYS(NOW())-TO_DAYS(date_due) ) = delay1 | 
            
              | 208 |                   OR  (TO_DAYS(NOW())-TO_DAYS(date_due) ) = delay2 | 
            
              | 209 |                   OR  (TO_DAYS(NOW())-TO_DAYS(date_due) ) = delay3 ) | 
            
              | 210 |                 GROUP BY items.itemnumber | 
            
              | 211 |                 "; | 
            
              | 212 |    my $sth = $dbh->prepare($query); | 
            
              | 213 |    $sth->execute(); | 
            
              | 214 |    my @results; | 
            
              | 215 |    while( my $issue = $sth->fetchrow_hashref() ) { | 
            
              | 216 |       if ( $issue->{'daysoverdue'} == $issue->{'delay1'} ) { | 
            
              | 217 |          $issue->{'level'} = 1; | 
            
              | 218 |       } elsif ( $issue->{'daysoverdue'} == $issue->{'delay2'} ) { | 
            
              | 219 |          $issue->{'level'} = 2; | 
            
              | 220 |       } elsif ( $issue->{'daysoverdue'} == $issue->{'delay3'} ) { | 
            
              | 221 |          $issue->{'level'} = 3; | 
            
              | 222 |       } else {  | 
            
              | 223 |       # this shouldn't ever happen, based our SQL criteria | 
            
              | 224 |       } | 
            
              | 225 |       push @results, $issue; | 
            
              | 226 |    } | 
            
              | 227 |    return @results; | 
            
              | 228 | } | 
            
              | 229 |  | 
            
              | 230 | sub GetPredueIssues { | 
            
              | 231 |    my $query = "SELECT borrowers.borrowernumber, borrowers.cardnumber, borrowers.title as patron_title, borrowers.firstname, borrowers.surname, | 
            
              | 232 |                 borrowers.phone, borrowers.email, borrowers.branchcode, biblio.biblionumber, biblio.title, items.barcode, issues.date_due, | 
            
              | 233 |                 issues.branchcode as site, branches.branchname as site_name | 
            
              | 234 |                 FROM borrowers JOIN issues USING (borrowernumber) | 
            
              | 235 |                 JOIN items USING (itemnumber) | 
            
              | 236 |                 JOIN biblio USING (biblionumber) | 
            
              | 237 |                 JOIN branches ON (issues.branchcode = branches.branchcode) | 
            
              | 238 |                 JOIN borrower_message_preferences USING (borrowernumber) | 
            
              | 239 |                 JOIN borrower_message_transport_preferences USING (borrower_message_preference_id) | 
            
              | 240 |                 JOIN message_attributes USING (message_attribute_id) | 
            
              | 241 |                 WHERE ( TO_DAYS( date_due ) - TO_DAYS( NOW() ) ) = days_in_advance | 
            
              | 242 |                 AND message_transport_type = 'phone' | 
            
              | 243 |                 AND message_name = 'Advance_Notice' | 
            
              | 244 |                 "; | 
            
              | 245 |    my $sth = $dbh->prepare($query); | 
            
              | 246 |    $sth->execute(); | 
            
              | 247 |    my @results; | 
            
              | 248 |    while( my $issue = $sth->fetchrow_hashref() ) { | 
            
              | 249 |       $issue->{'level'} = 1; # only one level for Predue notifications | 
            
              | 250 |       push @results, $issue; | 
            
              | 251 |    } | 
            
              | 252 |    return @results; | 
            
              | 253 | } | 
            
              | 254 |  | 
            
              | 255 | sub GetWaitingHolds { | 
            
              | 256 |    my $query = "SELECT borrowers.borrowernumber, borrowers.cardnumber, borrowers.title as patron_title, borrowers.firstname, borrowers.surname, | 
            
              | 257 |                 borrowers.phone, borrowers.email, borrowers.branchcode, biblio.biblionumber, biblio.title, items.barcode, reserves.waitingdate, | 
            
              | 258 |                 reserves.branchcode AS site, branches.branchname AS site_name, | 
            
              | 259 |                 TO_DAYS(NOW())-TO_DAYS(reserves.waitingdate) AS days_since_waiting | 
            
              | 260 |                 FROM borrowers JOIN reserves USING (borrowernumber) | 
            
              | 261 |                 JOIN items USING (itemnumber) | 
            
              | 262 |                 JOIN biblio ON (biblio.biblionumber = items.biblionumber) | 
            
              | 263 |                 JOIN branches ON (reserves.branchcode = branches.branchcode) | 
            
              | 264 |                 JOIN borrower_message_preferences USING (borrowernumber) | 
            
              | 265 |                 JOIN borrower_message_transport_preferences USING (borrower_message_preference_id) | 
            
              | 266 |                 JOIN message_attributes USING (message_attribute_id) | 
            
              | 267 |                 WHERE ( reserves.found = 'W' ) | 
            
              | 268 |                 AND message_transport_type = 'phone' | 
            
              | 269 |                 AND message_name = 'Hold_Filled' | 
            
              | 270 |                 "; | 
            
              | 271 |    my $pickupdelay = C4::Context->preference("ReservesMaxPickUpDelay"); | 
            
              | 272 |    my $sth = $dbh->prepare($query); | 
            
              | 273 |    $sth->execute(); | 
            
              | 274 |    my @results; | 
            
              | 275 |    while( my $issue = $sth->fetchrow_hashref() ) { | 
            
              | 276 |       my @waitingdate = split( /-/, $issue->{'waitingdate'} ); | 
            
              | 277 |       my @date_due = Add_Delta_Days( $waitingdate[0], $waitingdate[1], $waitingdate[2], $pickupdelay ); | 
            
              | 278 |       $issue->{'date_due'} = sprintf( "%04d-%02d-%02d", $date_due[0], $date_due[1], $date_due[2] ); | 
            
              | 279 |       $issue->{'level'} = 1; # only one level for Hold Waiting notifications | 
            
              | 280 |  | 
            
              | 281 |       my $days_to_subtract = 0; | 
            
              | 282 |       my $calendar = C4::Calendar->new( branchcode => $issue->{'site'} ); | 
            
              | 283 |       while( $calendar->isHoliday( reverse( Add_Delta_Days( $waitingdate[0], $waitingdate[1], $waitingdate[2], $days_to_subtract ) ) ) ) { | 
            
              | 284 |         $days_to_subtract++; | 
            
              | 285 |       } | 
            
              | 286 |       $issue->{'days_since_waiting'} = $issue->{'days_since_waiting'} - $days_to_subtract; | 
            
              | 287 |        | 
            
              | 288 |       if ( ( grep $_ eq $issue->{'days_since_waiting'}, @holds_waiting_days_to_call ) || !scalar( @holds_waiting_days_to_call ) ) { | 
            
              | 289 |           push @results, $issue; | 
            
              | 290 |       } | 
            
              | 291 |    } | 
            
              | 292 |    return @results; | 
            
              | 293 |  | 
            
              | 294 |  | 
            
              | 295 | } |