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 $help; |
56 |
my $outfile; |
57 |
|
58 |
# maps to convert I-tiva terms to Koha terms |
59 |
my $type_module_map = { 'PREOVERDUE' => 'circulation', |
60 |
'OVERDUE' => 'circulation', |
61 |
'RESERVE' => 'reserves', |
62 |
}; |
63 |
|
64 |
my $type_notice_map = { 'PREOVERDUE' => 'PREDUE_PHONE', |
65 |
'OVERDUE' => 'OVERDUE_PHONE', |
66 |
'RESERVE' => 'HOLD_PHONE', |
67 |
}; |
68 |
|
69 |
GetOptions( |
70 |
'o|output:s' => \$outfile, |
71 |
'v' => \$verbose, |
72 |
'lang:s' => \$language, |
73 |
'type:s' => \@types, |
74 |
'w|waiting-hold-day:s' => \@holds_waiting_days_to_call, |
75 |
'help|h' => \$help, |
76 |
); |
77 |
|
78 |
$language = uc($language); |
79 |
|
80 |
pod2usage( -verbose => 1 ) if $help; |
81 |
|
82 |
# output log or STDOUT |
83 |
if (defined $outfile) { |
84 |
open (OUT, ">$outfile") || die ("Cannot open output file"); |
85 |
} else { |
86 |
print "No output file defined; printing to STDOUT\n" if (defined $verbose); |
87 |
open(OUT, ">&STDOUT") || die ("Couldn't duplicate STDOUT: $!"); |
88 |
} |
89 |
|
90 |
my $format = 'V'; # format for phone notifications |
91 |
|
92 |
foreach my $type (@types) { |
93 |
$type = uc($type); #just in case lower or mixed-case was supplied |
94 |
my $module = $type_module_map->{$type}; #since the module is required to get the letter |
95 |
my $code = $type_notice_map->{$type}; #to get the Koha name of the notice |
96 |
|
97 |
my @loop; |
98 |
if ($type eq 'OVERDUE') { |
99 |
@loop = GetOverdueIssues(); |
100 |
} elsif ($type eq 'PREOVERDUE') { |
101 |
@loop = GetPredueIssues(); |
102 |
} elsif ($type eq 'RESERVE') { |
103 |
@loop = GetWaitingHolds(); |
104 |
} else { |
105 |
print "Unknown or unsupported message type $type; skipping...\n" if (defined $verbose); |
106 |
next; |
107 |
} |
108 |
|
109 |
foreach my $issues (@loop) { |
110 |
my $date = C4::Dates->new($issues->{'date_due'}, 'iso'); |
111 |
my $due_date = $date->output('metric'); |
112 |
|
113 |
# gets the placeholder message, and enqueues the letter |
114 |
my $letter = getletter($module, $code); |
115 |
die "No letter found for type $type!... dying\n" unless $letter; |
116 |
# covers basic variable parsing in letter |
117 |
$letter = C4::Letters::parseletter($letter, 'borrowers', $issues->{'borrowernumber'}); |
118 |
$letter = C4::Letters::parseletter($letter, 'biblio', $issues->{'biblionumber'}); |
119 |
$letter = C4::Letters::parseletter($letter, 'biblioitems', $issues->{'biblionumber'}); |
120 |
|
121 |
my $message_id = 0; |
122 |
if ($outfile) { |
123 |
$message_id = C4::Letters::EnqueueLetter( { letter => $letter, |
124 |
borrowernumber => $issues->{'borrowernumber'}, |
125 |
message_transport_type => 'phone', |
126 |
}); |
127 |
} |
128 |
|
129 |
print OUT "\"$format\",\"$language\",\"$type\",\"$issues->{level}\",\"$issues->{cardnumber}\",\"$issues->{patron_title}\",\"$issues->{firstname}\","; |
130 |
print OUT "\"$issues->{surname}\",\"$issues->{phone}\",\"$issues->{email}\",\"$issues->{branchcode}\","; |
131 |
print OUT "\"$issues->{site}\",\"$issues->{site_name}\",\"$issues->{barcode}\",\"$due_date\",\"$issues->{title}\",\"$message_id\"\n"; |
132 |
} |
133 |
} |
134 |
|
135 |
|
136 |
=head1 NAME |
137 |
|
138 |
TalkingTech_itiva_outbound.pl |
139 |
|
140 |
=head1 SYNOPSIS |
141 |
|
142 |
TalkingTech_itiva_outbound.pl |
143 |
TalkingTech_itiva_outbound.pl --type=OVERDUE -w 0 -w 2 -w 6 --output=/tmp/talkingtech/outbound.csv |
144 |
TalkingTech_itiva_outbound.pl --type=RESERVE --type=PREOVERDUE --lang=FR |
145 |
|
146 |
|
147 |
Script to generate Spec C outbound notifications file for Talking Tech i-tiva |
148 |
phone notification system. |
149 |
|
150 |
=item B<--help> B<-h> |
151 |
|
152 |
Prints this help |
153 |
|
154 |
=item B<-v> |
155 |
|
156 |
Provide verbose log information. |
157 |
|
158 |
=item B<--output> B<-o> |
159 |
|
160 |
Destination for outbound notifications file (CSV format). If no value is specified, |
161 |
output is dumped to screen. |
162 |
|
163 |
=item B<--lang> |
164 |
|
165 |
Sets the language for all outbound messages. Currently supported values are EN, FR and ES. |
166 |
If no value is specified, EN will be used by default. |
167 |
|
168 |
=item B<--type> |
169 |
|
170 |
REQUIRED. Sets which messaging types are to be used. Can be given multiple times, to |
171 |
specify multiple types in a single output file. Currently supported values are RESERVE, PREOVERDUE |
172 |
and OVERDUE. If no value is given, this script will not produce any outbound notifications. |
173 |
|
174 |
=item B<--waiting-hold-day> B<-w> |
175 |
|
176 |
OPTIONAL for --type=RESERVE. Sets the days after a hold has been set to waiting on which to call. Use |
177 |
switch as many times as desired. For example, passing "-w 0 -w 2 -w 6" will cause calls to be placed |
178 |
on the day the hold was set to waiting, 2 days after the waiting date, and 6 days after. See example above. |
179 |
If this switch is not used with --type=RESERVE, calls will be placed every day until the waiting reserve |
180 |
is picked up or canceled. |
181 |
|
182 |
=cut |
183 |
|
184 |
sub GetOverdueIssues { |
185 |
my $query = "SELECT borrowers.borrowernumber, borrowers.cardnumber, borrowers.title as patron_title, borrowers.firstname, borrowers.surname, |
186 |
borrowers.phone, borrowers.email, borrowers.branchcode, biblio.biblionumber, biblio.title, items.barcode, issues.date_due, |
187 |
max(overduerules.branchcode) as rulebranch, TO_DAYS(NOW())-TO_DAYS(date_due) as daysoverdue, delay1, delay2, delay3, |
188 |
issues.branchcode as site, branches.branchname as site_name |
189 |
FROM borrowers JOIN issues USING (borrowernumber) |
190 |
JOIN items USING (itemnumber) |
191 |
JOIN biblio USING (biblionumber) |
192 |
JOIN branches ON (issues.branchcode = branches.branchcode) |
193 |
JOIN overduerules USING (categorycode) |
194 |
WHERE ( overduerules.branchcode = borrowers.branchcode or overduerules.branchcode = '') |
195 |
AND ( (TO_DAYS(NOW())-TO_DAYS(date_due) ) = delay1 |
196 |
OR (TO_DAYS(NOW())-TO_DAYS(date_due) ) = delay2 |
197 |
OR (TO_DAYS(NOW())-TO_DAYS(date_due) ) = delay3 ) |
198 |
GROUP BY items.itemnumber |
199 |
"; |
200 |
my $sth = $dbh->prepare($query); |
201 |
$sth->execute(); |
202 |
my @results; |
203 |
while( my $issue = $sth->fetchrow_hashref() ) { |
204 |
if ( $issue->{'daysoverdue'} == $issue->{'delay1'} ) { |
205 |
$issue->{'level'} = 1; |
206 |
} elsif ( $issue->{'daysoverdue'} == $issue->{'delay2'} ) { |
207 |
$issue->{'level'} = 2; |
208 |
} elsif ( $issue->{'daysoverdue'} == $issue->{'delay3'} ) { |
209 |
$issue->{'level'} = 3; |
210 |
} else { |
211 |
# this shouldn't ever happen, based our SQL criteria |
212 |
} |
213 |
push @results, $issue; |
214 |
} |
215 |
return @results; |
216 |
} |
217 |
|
218 |
sub GetPredueIssues { |
219 |
my $query = "SELECT borrowers.borrowernumber, borrowers.cardnumber, borrowers.title as patron_title, borrowers.firstname, borrowers.surname, |
220 |
borrowers.phone, borrowers.email, borrowers.branchcode, biblio.biblionumber, biblio.title, items.barcode, issues.date_due, |
221 |
issues.branchcode as site, branches.branchname as site_name |
222 |
FROM borrowers JOIN issues USING (borrowernumber) |
223 |
JOIN items USING (itemnumber) |
224 |
JOIN biblio USING (biblionumber) |
225 |
JOIN branches ON (issues.branchcode = branches.branchcode) |
226 |
JOIN borrower_message_preferences USING (borrowernumber) |
227 |
JOIN borrower_message_transport_preferences USING (borrower_message_preference_id) |
228 |
JOIN message_attributes USING (message_attribute_id) |
229 |
WHERE ( TO_DAYS( date_due ) - TO_DAYS( NOW() ) ) = days_in_advance |
230 |
AND message_transport_type = 'phone' |
231 |
AND message_name = 'Advance_Notice' |
232 |
"; |
233 |
my $sth = $dbh->prepare($query); |
234 |
$sth->execute(); |
235 |
my @results; |
236 |
while( my $issue = $sth->fetchrow_hashref() ) { |
237 |
$issue->{'level'} = 1; # only one level for Predue notifications |
238 |
push @results, $issue; |
239 |
} |
240 |
return @results; |
241 |
} |
242 |
|
243 |
sub GetWaitingHolds { |
244 |
my $query = "SELECT borrowers.borrowernumber, borrowers.cardnumber, borrowers.title as patron_title, borrowers.firstname, borrowers.surname, |
245 |
borrowers.phone, borrowers.email, borrowers.branchcode, biblio.biblionumber, biblio.title, items.barcode, reserves.waitingdate, |
246 |
reserves.branchcode AS site, branches.branchname AS site_name, |
247 |
TO_DAYS(NOW())-TO_DAYS(reserves.waitingdate) AS days_since_waiting |
248 |
FROM borrowers JOIN reserves USING (borrowernumber) |
249 |
JOIN items USING (itemnumber) |
250 |
JOIN biblio ON (biblio.biblionumber = items.biblionumber) |
251 |
JOIN branches ON (reserves.branchcode = branches.branchcode) |
252 |
JOIN borrower_message_preferences USING (borrowernumber) |
253 |
JOIN borrower_message_transport_preferences USING (borrower_message_preference_id) |
254 |
JOIN message_attributes USING (message_attribute_id) |
255 |
WHERE ( reserves.found = 'W' ) |
256 |
AND message_transport_type = 'phone' |
257 |
AND message_name = 'Hold_Filled' |
258 |
"; |
259 |
my $pickupdelay = C4::Context->preference("ReservesMaxPickUpDelay"); |
260 |
my $sth = $dbh->prepare($query); |
261 |
$sth->execute(); |
262 |
my @results; |
263 |
while( my $issue = $sth->fetchrow_hashref() ) { |
264 |
my @waitingdate = split( /-/, $issue->{'waitingdate'} ); |
265 |
my @date_due = Add_Delta_Days( $waitingdate[0], $waitingdate[1], $waitingdate[2], $pickupdelay ); |
266 |
$issue->{'date_due'} = sprintf( "%04d-%02d-%02d", $date_due[0], $date_due[1], $date_due[2] ); |
267 |
$issue->{'level'} = 1; # only one level for Hold Waiting notifications |
268 |
|
269 |
my $days_to_subtract = 0; |
270 |
my $calendar = C4::Calendar->new( branchcode => $issue->{'site'} ); |
271 |
while( $calendar->isHoliday( reverse( Add_Delta_Days( $waitingdate[0], $waitingdate[1], $waitingdate[2], $days_to_subtract ) ) ) ) { |
272 |
$days_to_subtract++; |
273 |
} |
274 |
$issue->{'days_since_waiting'} = $issue->{'days_since_waiting'} - $days_to_subtract; |
275 |
|
276 |
if ( ( grep $_ eq $issue->{'days_since_waiting'}, @holds_waiting_days_to_call ) || !scalar( @holds_waiting_days_to_call ) ) { |
277 |
push @results, $issue; |
278 |
} |
279 |
} |
280 |
return @results; |
281 |
|
282 |
|
283 |
} |