3 # Copyright (C) 2011 ByWater Solutions
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
25 # find Koha's Perl modules
26 # test carefully before changing this
28 eval { require "$FindBin::Bin/../kohalib.pl" };
33 use Date::Calc qw(Add_Delta_Days);
43 pod2usage( -verbose => 2 );
48 "TalkingTechItivaPhoneNotification system preference not activated... dying\n"
49 unless ( C4::Context->preference("TalkingTechItivaPhoneNotification") );
52 my $dbh = C4::Context->dbh;
58 my @holds_waiting_days_to_call;
63 # maps to convert I-tiva terms to Koha terms
64 my $type_module_map = {
65 'PREOVERDUE' => 'circulation',
66 'OVERDUE' => 'circulation',
67 'RESERVE' => 'reserves',
70 my $type_notice_map = {
71 'PREOVERDUE' => 'PREDUE',
72 'OVERDUE' => 'OVERDUE',
77 'o|output:s' => \$outfile,
79 'lang:s' => \$language,
81 'w|waiting-hold-day:s' => \@holds_waiting_days_to_call,
82 'c|code|library-code:s' => \$library_code,
86 $language = uc($language);
89 pod2usage( -verbose => 1 ) if $help;
91 # output log or STDOUT
93 if ( defined $outfile ) {
94 open( $OUT, '>', "$outfile" ) || die("Cannot open output file");
97 print "No output file defined; printing to STDOUT\n"
98 if ( defined $verbose );
99 open( $OUT, '>', "&STDOUT" ) || die("Couldn't duplicate STDOUT: $!");
102 my $format = 'V'; # format for phone notifications
104 foreach my $type (@types) {
105 $type = uc($type); #just in case lower or mixed-case was supplied
107 $type_module_map->{$type}; #since the module is required to get the letter
108 my $code = $type_notice_map->{$type}; #to get the Koha name of the notice
111 if ( $type eq 'OVERDUE' ) {
112 @loop = GetOverdueIssues();
114 elsif ( $type eq 'PREOVERDUE' ) {
115 @loop = GetPredueIssues();
117 elsif ( $type eq 'RESERVE' ) {
118 @loop = GetWaitingHolds();
121 print "Unknown or unsupported message type $type; skipping...\n"
122 if ( defined $verbose );
126 foreach my $issues (@loop) {
127 my $date = C4::Dates->new( $issues->{'date_due'}, 'iso' );
128 my $due_date = $date->output('metric');
130 my $letter = C4::Letters::GetPreparedLetter(
132 letter_code => $code,
134 borrowers => $issues->{'borrowernumber'},
135 biblio => $issues->{'biblionumber'},
136 biblioitems => $issues->{'biblionumber'},
138 message_transport_type => 'phone',
141 die "No letter found for type $type!... dying\n" unless $letter;
145 $message_id = C4::Letters::EnqueueLetter(
148 borrowernumber => $issues->{'borrowernumber'},
149 message_transport_type => 'phone',
155 "\"$format\",\"$language\",\"$type\",\"$issues->{level}\",\"$issues->{cardnumber}\",\"$issues->{patron_title}\",\"$issues->{firstname}\",";
157 "\"$issues->{surname}\",\"$issues->{phone}\",\"$issues->{email}\",\"$library_code\",";
159 "\"$issues->{site}\",\"$issues->{site_name}\",\"$issues->{barcode}\",\"$due_date\",\"$issues->{title}\",\"$message_id\"\n";
165 TalkingTech_itiva_outbound.pl
169 TalkingTech_itiva_outbound.pl
170 TalkingTech_itiva_outbound.pl --type=OVERDUE -w 0 -w 2 -w 6 --output=/tmp/talkingtech/outbound.csv
171 TalkingTech_itiva_outbound.pl --type=RESERVE --type=PREOVERDUE --lang=FR
174 Script to generate Spec C outbound notifications file for Talking Tech i-tiva
175 phone notification system.
177 =item B<--help> B<-h>
183 Provide verbose log information.
185 =item B<--output> B<-o>
187 Destination for outbound notifications file (CSV format). If no value is specified,
188 output is dumped to screen.
192 Sets the language for all outbound messages. Currently supported values are EN, FR and ES.
193 If no value is specified, EN will be used by default.
197 REQUIRED. Sets which messaging types are to be used. Can be given multiple times, to
198 specify multiple types in a single output file. Currently supported values are RESERVE, PREOVERDUE
199 and OVERDUE. If no value is given, this script will not produce any outbound notifications.
201 =item B<--waiting-hold-day> B<-w>
203 OPTIONAL for --type=RESERVE. Sets the days after a hold has been set to waiting on which to call. Use
204 switch as many times as desired. For example, passing "-w 0 -w 2 -w 6" will cause calls to be placed
205 on the day the hold was set to waiting, 2 days after the waiting date, and 6 days after. See example above.
206 If this switch is not used with --type=RESERVE, calls will be placed every day until the waiting reserve
207 is picked up or canceled.
209 =item B<--library-code> B<--code> B<-c>
212 The code of the source library of the message.
213 The library code is used to group notices together for
214 consortium purposes and apply library specific settings, such as
215 prompts, to those notices.
216 This field can be blank if all messages are from a single library.
220 sub GetOverdueIssues {
222 "SELECT borrowers.borrowernumber, borrowers.cardnumber, borrowers.title as patron_title, borrowers.firstname, borrowers.surname,
223 borrowers.phone, borrowers.email, borrowers.branchcode, biblio.biblionumber, biblio.title, items.barcode, issues.date_due,
224 max(overduerules.branchcode) as rulebranch, TO_DAYS(NOW())-TO_DAYS(date_due) as daysoverdue, delay1, delay2, delay3,
225 issues.branchcode as site, branches.branchname as site_name
226 FROM borrowers JOIN issues USING (borrowernumber)
227 JOIN items USING (itemnumber)
228 JOIN biblio USING (biblionumber)
229 JOIN branches ON (issues.branchcode = branches.branchcode)
230 JOIN overduerules USING (categorycode)
231 WHERE ( overduerules.branchcode = borrowers.branchcode or overduerules.branchcode = '')
232 AND ( (TO_DAYS(NOW())-TO_DAYS(date_due) ) = delay1
233 OR (TO_DAYS(NOW())-TO_DAYS(date_due) ) = delay2
234 OR (TO_DAYS(NOW())-TO_DAYS(date_due) ) = delay3 )
235 GROUP BY items.itemnumber
237 my $sth = $dbh->prepare($query);
240 while ( my $issue = $sth->fetchrow_hashref() ) {
241 if ( $issue->{'daysoverdue'} == $issue->{'delay1'} ) {
242 $issue->{'level'} = 1;
244 elsif ( $issue->{'daysoverdue'} == $issue->{'delay2'} ) {
245 $issue->{'level'} = 2;
247 elsif ( $issue->{'daysoverdue'} == $issue->{'delay3'} ) {
248 $issue->{'level'} = 3;
252 # this shouldn't ever happen, based our SQL criteria
254 push @results, $issue;
259 sub GetPredueIssues {
261 "SELECT borrowers.borrowernumber, borrowers.cardnumber, borrowers.title as patron_title, borrowers.firstname, borrowers.surname,
262 borrowers.phone, borrowers.email, borrowers.branchcode, biblio.biblionumber, biblio.title, items.barcode, issues.date_due,
263 issues.branchcode as site, branches.branchname as site_name
264 FROM borrowers JOIN issues USING (borrowernumber)
265 JOIN items USING (itemnumber)
266 JOIN biblio USING (biblionumber)
267 JOIN branches ON (issues.branchcode = branches.branchcode)
268 JOIN borrower_message_preferences USING (borrowernumber)
269 JOIN borrower_message_transport_preferences USING (borrower_message_preference_id)
270 JOIN message_attributes USING (message_attribute_id)
271 WHERE ( TO_DAYS( date_due ) - TO_DAYS( NOW() ) ) = days_in_advance
272 AND message_transport_type = 'phone'
273 AND message_name = 'Advance_Notice'
275 my $sth = $dbh->prepare($query);
278 while ( my $issue = $sth->fetchrow_hashref() ) {
279 $issue->{'level'} = 1; # only one level for Predue notifications
280 push @results, $issue;
285 sub GetWaitingHolds {
287 "SELECT borrowers.borrowernumber, borrowers.cardnumber, borrowers.title as patron_title, borrowers.firstname, borrowers.surname,
288 borrowers.phone, borrowers.email, borrowers.branchcode, biblio.biblionumber, biblio.title, items.barcode, reserves.waitingdate,
289 reserves.branchcode AS site, branches.branchname AS site_name,
290 TO_DAYS(NOW())-TO_DAYS(reserves.waitingdate) AS days_since_waiting
291 FROM borrowers JOIN reserves USING (borrowernumber)
292 JOIN items USING (itemnumber)
293 JOIN biblio ON (biblio.biblionumber = items.biblionumber)
294 JOIN branches ON (reserves.branchcode = branches.branchcode)
295 JOIN borrower_message_preferences USING (borrowernumber)
296 JOIN borrower_message_transport_preferences USING (borrower_message_preference_id)
297 JOIN message_attributes USING (message_attribute_id)
298 WHERE ( reserves.found = 'W' )
299 AND message_transport_type = 'phone'
300 AND message_name = 'Hold_Filled'
302 my $pickupdelay = C4::Context->preference("ReservesMaxPickUpDelay");
303 my $sth = $dbh->prepare($query);
306 while ( my $issue = $sth->fetchrow_hashref() ) {
307 my @waitingdate = split( /-/, $issue->{'waitingdate'} );
309 Add_Delta_Days( $waitingdate[0], $waitingdate[1], $waitingdate[2],
311 $issue->{'date_due'} =
312 sprintf( "%04d-%02d-%02d", $date_due[0], $date_due[1], $date_due[2] );
313 $issue->{'level'} = 1; # only one level for Hold Waiting notifications
315 my $days_to_subtract = 0;
316 my $calendar = C4::Calendar->new( branchcode => $issue->{'site'} );
318 $calendar->isHoliday(
321 $waitingdate[0], $waitingdate[1],
322 $waitingdate[2], $days_to_subtract
330 $issue->{'days_since_waiting'} =
331 $issue->{'days_since_waiting'} - $days_to_subtract;
335 grep $_ eq $issue->{'days_since_waiting'},
336 @holds_waiting_days_to_call
338 || !scalar(@holds_waiting_days_to_call)
341 push @results, $issue;