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 |
=cut |
186 |
|
187 |
sub GetOverdueIssues { |
188 |
my $query = "SELECT borrowers.borrowernumber, borrowers.cardnumber, borrowers.title as patron_title, borrowers.firstname, borrowers.surname, |
189 |
borrowers.phone, borrowers.email, borrowers.branchcode, biblio.biblionumber, biblio.title, items.barcode, issues.date_due, |
190 |
max(overduerules.branchcode) as rulebranch, TO_DAYS(NOW())-TO_DAYS(date_due) as daysoverdue, delay1, delay2, delay3, |
191 |
issues.branchcode as site, branches.branchname as site_name |
192 |
FROM borrowers JOIN issues USING (borrowernumber) |
193 |
JOIN items USING (itemnumber) |
194 |
JOIN biblio USING (biblionumber) |
195 |
JOIN branches ON (issues.branchcode = branches.branchcode) |
196 |
JOIN overduerules USING (categorycode) |
197 |
WHERE ( overduerules.branchcode = borrowers.branchcode or overduerules.branchcode = '') |
198 |
AND ( (TO_DAYS(NOW())-TO_DAYS(date_due) ) = delay1 |
199 |
OR (TO_DAYS(NOW())-TO_DAYS(date_due) ) = delay2 |
200 |
OR (TO_DAYS(NOW())-TO_DAYS(date_due) ) = delay3 ) |
201 |
GROUP BY items.itemnumber |
202 |
"; |
203 |
my $sth = $dbh->prepare($query); |
204 |
$sth->execute(); |
205 |
my @results; |
206 |
while( my $issue = $sth->fetchrow_hashref() ) { |
207 |
if ( $issue->{'daysoverdue'} == $issue->{'delay1'} ) { |
208 |
$issue->{'level'} = 1; |
209 |
} elsif ( $issue->{'daysoverdue'} == $issue->{'delay2'} ) { |
210 |
$issue->{'level'} = 2; |
211 |
} elsif ( $issue->{'daysoverdue'} == $issue->{'delay3'} ) { |
212 |
$issue->{'level'} = 3; |
213 |
} else { |
214 |
# this shouldn't ever happen, based our SQL criteria |
215 |
} |
216 |
push @results, $issue; |
217 |
} |
218 |
return @results; |
219 |
} |
220 |
|
221 |
sub GetPredueIssues { |
222 |
my $query = "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 |
issues.branchcode as site, branches.branchname as site_name |
225 |
FROM borrowers JOIN issues USING (borrowernumber) |
226 |
JOIN items USING (itemnumber) |
227 |
JOIN biblio USING (biblionumber) |
228 |
JOIN branches ON (issues.branchcode = branches.branchcode) |
229 |
JOIN borrower_message_preferences USING (borrowernumber) |
230 |
JOIN borrower_message_transport_preferences USING (borrower_message_preference_id) |
231 |
JOIN message_attributes USING (message_attribute_id) |
232 |
WHERE ( TO_DAYS( date_due ) - TO_DAYS( NOW() ) ) = days_in_advance |
233 |
AND message_transport_type = 'phone' |
234 |
AND message_name = 'Advance_Notice' |
235 |
"; |
236 |
my $sth = $dbh->prepare($query); |
237 |
$sth->execute(); |
238 |
my @results; |
239 |
while( my $issue = $sth->fetchrow_hashref() ) { |
240 |
$issue->{'level'} = 1; # only one level for Predue notifications |
241 |
push @results, $issue; |
242 |
} |
243 |
return @results; |
244 |
} |
245 |
|
246 |
sub GetWaitingHolds { |
247 |
my $query = "SELECT borrowers.borrowernumber, borrowers.cardnumber, borrowers.title as patron_title, borrowers.firstname, borrowers.surname, |
248 |
borrowers.phone, borrowers.email, borrowers.branchcode, biblio.biblionumber, biblio.title, items.barcode, reserves.waitingdate, |
249 |
reserves.branchcode AS site, branches.branchname AS site_name, |
250 |
TO_DAYS(NOW())-TO_DAYS(reserves.waitingdate) AS days_since_waiting |
251 |
FROM borrowers JOIN reserves USING (borrowernumber) |
252 |
JOIN items USING (itemnumber) |
253 |
JOIN biblio ON (biblio.biblionumber = items.biblionumber) |
254 |
JOIN branches ON (reserves.branchcode = branches.branchcode) |
255 |
JOIN borrower_message_preferences USING (borrowernumber) |
256 |
JOIN borrower_message_transport_preferences USING (borrower_message_preference_id) |
257 |
JOIN message_attributes USING (message_attribute_id) |
258 |
WHERE ( reserves.found = 'W' ) |
259 |
AND message_transport_type = 'phone' |
260 |
AND message_name = 'Hold_Filled' |
261 |
"; |
262 |
my $pickupdelay = C4::Context->preference("ReservesMaxPickUpDelay"); |
263 |
my $sth = $dbh->prepare($query); |
264 |
$sth->execute(); |
265 |
my @results; |
266 |
while( my $issue = $sth->fetchrow_hashref() ) { |
267 |
my @waitingdate = split( /-/, $issue->{'waitingdate'} ); |
268 |
my @date_due = Add_Delta_Days( $waitingdate[0], $waitingdate[1], $waitingdate[2], $pickupdelay ); |
269 |
$issue->{'date_due'} = sprintf( "%04d-%02d-%02d", $date_due[0], $date_due[1], $date_due[2] ); |
270 |
$issue->{'level'} = 1; # only one level for Hold Waiting notifications |
271 |
|
272 |
my $days_to_subtract = 0; |
273 |
my $calendar = C4::Calendar->new( branchcode => $issue->{'site'} ); |
274 |
while( $calendar->isHoliday( reverse( Add_Delta_Days( $waitingdate[0], $waitingdate[1], $waitingdate[2], $days_to_subtract ) ) ) ) { |
275 |
$days_to_subtract++; |
276 |
} |
277 |
$issue->{'days_since_waiting'} = $issue->{'days_since_waiting'} - $days_to_subtract; |
278 |
|
279 |
if ( ( grep $_ eq $issue->{'days_since_waiting'}, @holds_waiting_days_to_call ) || !scalar( @holds_waiting_days_to_call ) ) { |
280 |
push @results, $issue; |
281 |
} |
282 |
} |
283 |
return @results; |
284 |
|
285 |
|
286 |
} |