X-Git-Url: http://koha-dev.rot13.org:8081/gitweb/?a=blobdiff_plain;f=misc%2Fcronjobs%2Foverdue_notices.pl;h=37774b523ec26903e1e110487543b4de2800158e;hb=624b1562d8567dabd23cb4e1ac7e34615e604edc;hp=b35879735d37455877df6a1ba371e5c67c9a1cf6;hpb=d339abf0bfc334a819818bbb9b4c31f23326cbd1;p=koha_fer diff --git a/misc/cronjobs/overdue_notices.pl b/misc/cronjobs/overdue_notices.pl index b35879735d..37774b523e 100755 --- a/misc/cronjobs/overdue_notices.pl +++ b/misc/cronjobs/overdue_notices.pl @@ -1,6 +1,7 @@ -#!/usr/bin/perl -w +#!/usr/bin/perl # Copyright 2008 Liblime +# Copyright 2010 BibLibre # # This file is part of Koha. # @@ -13,9 +14,9 @@ # 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., 59 Temple Place, -# Suite 330, Boston, MA 02111-1307 USA +# 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. use strict; use warnings; @@ -28,14 +29,17 @@ BEGIN { eval { require "$FindBin::Bin/../kohalib.pl" }; } +use Getopt::Long; +use Pod::Usage; +use Text::CSV_XS; +use Locale::Currency::Format 1.28; +use Encode; + use C4::Context; use C4::Dates qw/format_date/; use C4::Debug; use C4::Letters; - -use Getopt::Long; -use Pod::Usage; -use Text::CSV_XS; +use C4::Overdues qw(GetFine); =head1 NAME @@ -52,6 +56,7 @@ overdue_notices.pl [ -n ] [ -library ] [ -library ...] -max maximum days overdue to deal with -library only deal with overdues from this library (repeatable : several libraries can be given) -csv populate CSV file + -html Output html to file -itemscontent item information in templates -borcat category code that must be included -borcatout category code that must be excluded @@ -98,13 +103,19 @@ Produces CSV data. if -n (no mail) flag is set, then this CSV data is sent to standard out or to a filename if provided. Otherwise, only overdues that could not be emailed are sent in CSV format to the admin. +=item B<-html> + +Produces html data. if patron does not have a mail address or +-n (no mail) flag is set, an html file is generated in the specified +directory. This can be downloaded or futher processed by library staff. + =item B<-itemscontent> comma separated list of fields that get substituted into templates in places of the EEitems.contentEE placeholder. This -defaults to issuedate,title,barcode,author +defaults to due date,title,barcode,author -Other possible values come from fields in the biblios, items, and +Other possible values come from fields in the biblios, items and issues tables. =item B<-borcat> @@ -242,9 +253,10 @@ my $nomail = 0; my $MAX = 90; my @branchcodes; # Branch(es) passed as parameter my $csvfilename; +my $htmlfilename; my $triggered = 0; my $listall = 0; -my $itemscontent = join( ',', qw( issuedate title barcode author ) ); +my $itemscontent = join( ',', qw( date_due title barcode author itemnumber ) ); my @myborcat; my @myborcatout; @@ -256,6 +268,7 @@ GetOptions( 'max=s' => \$MAX, 'library=s' => \@branchcodes, 'csv:s' => \$csvfilename, # this optional argument gets '' if not supplied. + 'html:s' => \$htmlfilename, # this optional argument gets '' if not supplied. 'itemscontent=s' => \$itemscontent, 'list-all' => \$listall, 't|triggered' => \$triggered, @@ -276,6 +289,8 @@ my $branchcount = scalar(@overduebranches); my $overduebranch_word = scalar @overduebranches > 1 ? 'branches' : 'branch'; my $branchcodes_word = scalar @branchcodes > 1 ? 'branches' : 'branch'; +my $PrintNoticesMaxLines = C4::Context->preference('PrintNoticesMaxLines'); + if ($branchcount) { $verbose and warn "Found $branchcount $overduebranch_word with first message enabled: " . join( ', ', map { "'$_'" } @overduebranches ), "\n"; } else { @@ -310,6 +325,7 @@ my @item_content_fields = split( /,/, $itemscontent ); binmode( STDOUT, ":utf8" ); + our $csv; # the Text::CSV_XS object our $csv_fh; # the filehandle to the CSV file. if ( defined $csvfilename ) { @@ -328,6 +344,28 @@ if ( defined $csvfilename ) { } @branches = @overduebranches unless @branches; +our $html_fh; +if ( defined $htmlfilename ) { + if ( $htmlfilename eq '' ) { + $html_fh = *STDOUT; + } else { + my $today = C4::Dates->new(); + open $html_fh, ">",File::Spec->catdir ($htmlfilename,"notices-".$today->output('iso').".html"); + } + + print $html_fh "\n"; + print $html_fh "\n"; + print $html_fh "\n"; + print $html_fh "\n"; + print $html_fh "\n"; +} + foreach my $branchcode (@branches) { my $branch_details = C4::Branch::GetBranchDetail($branchcode); @@ -337,10 +375,11 @@ foreach my $branchcode (@branches) { $verbose and warn sprintf "branchcode : '%s' using %s\n", $branchcode, $admin_email_address; my $sth2 = $dbh->prepare( <<'END_SQL' ); -SELECT biblio.*, items.*, issues.*, TO_DAYS(NOW())-TO_DAYS(date_due) AS days_overdue - FROM issues,items,biblio +SELECT biblio.*, items.*, issues.*, biblioitems.itemtype, TO_DAYS(NOW())-TO_DAYS(date_due) AS days_overdue + FROM issues,items,biblio, biblioitems WHERE items.itemnumber=issues.itemnumber AND biblio.biblionumber = items.biblionumber + AND biblio.biblionumber = biblioitems.biblionumber AND issues.borrowernumber = ? AND TO_DAYS(NOW())-TO_DAYS(date_due) BETWEEN ? and ? END_SQL @@ -422,6 +461,7 @@ END_SQL $verbose and warn "borrower $firstname, $lastname ($borrowernumber) has $itemcount items triggering level $i."; my $letter = C4::Letters::getletter( 'circulation', $overdue_rules->{"letter$i"} ); + unless ($letter) { $verbose and warn "Message '$overdue_rules->{letter$i}' content not found"; @@ -436,27 +476,43 @@ END_SQL C4::Members::DebarMember($borrowernumber); $verbose and warn "debarring $borrowernumber $firstname $lastname\n"; } - $sth2->execute( ($listall) ? ( $borrowernumber , 1 , $MAX ) : ( $borrowernumber, $mindays, $maxdays ) ); + my @params = ($listall ? ( $borrowernumber , 1 , $MAX ) : ( $borrowernumber, $mindays, $maxdays )); + $verbose and warn "STH2 PARAMS: borrowernumber = $borrowernumber, mindays = $mindays, maxdays = $maxdays"; + $sth2->execute(@params); my $itemcount = 0; my $titles = ""; + my @items = (); + + my $i = 0; + my $exceededPrintNoticesMaxLines = 0; while ( my $item_info = $sth2->fetchrow_hashref() ) { + if ( ( !$email || $nomail ) && $PrintNoticesMaxLines && $i >= $PrintNoticesMaxLines ) { + $exceededPrintNoticesMaxLines = 1; + last; + } + $i++; my @item_info = map { $_ =~ /^date|date$/ ? format_date( $item_info->{$_} ) : $item_info->{$_} || '' } @item_content_fields; $titles .= join("\t", @item_info) . "\n"; $itemcount++; + push @items, { itemnumber => $item_info->{'itemnumber'}, biblionumber => $item_info->{'biblionumber'} }; } $sth2->finish; - $letter = parse_letter( - { letter => $letter, - borrowernumber => $borrowernumber, - branchcode => $branchcode, - substitute => { - bib => $branch_details->{'branchname'}, - 'items.content' => $titles - } + { letter => $letter, + borrowernumber => $borrowernumber, + branchcode => $branchcode, + items => \@items, + substitute => { # this appears to be a hack to overcome incomplete features in this code. + bib => $branch_details->{'branchname'}, # maybe 'bib' is a typo for 'lib'? + 'items.content' => $titles + } } ); - + + if ( $exceededPrintNoticesMaxLines ) { + $letter->{'content'} .= "List too long for form; please check your account online for a complete list of your overdue items."; + } + my @misses = grep { /./ } map { /^([^>]*)[>]+/; ( $1 || '' ); } split /\{'content'}; if (@misses) { $verbose and warn "The following terms were not matched and replaced: \n\t" . join "\n\t", @misses; @@ -479,7 +535,7 @@ END_SQL email => $email, itemcount => $itemcount, titles => $titles, - outputformat => defined $csvfilename ? 'csv' : '', + outputformat => defined $csvfilename ? 'csv' : defined $htmlfilename ? 'html' : '', } ); } else { @@ -507,7 +563,7 @@ END_SQL email => $email, itemcount => $itemcount, titles => $titles, - outputformat => defined $csvfilename ? 'csv' : '', + outputformat => defined $csvfilename ? 'csv' : defined $htmlfilename ? 'html' : '', } ); } @@ -518,43 +574,53 @@ END_SQL } if (@output_chunks) { - if ($nomail) { - if ( defined $csvfilename ) { - print $csv_fh @output_chunks; - } else { + if ( defined $csvfilename ) { + print $csv_fh @output_chunks; + } + elsif ( defined $htmlfilename ) { + print $html_fh @output_chunks; + } + elsif ($nomail){ local $, = "\f"; # pagebreak print @output_chunks; - } - } else { - my $attachment = { - filename => defined $csvfilename ? 'attachment.csv' : 'attachment.txt', - type => 'text/plain', - content => join( "\n", @output_chunks ) - }; - - my $letter = { - title => 'Overdue Notices', - content => 'These messages were not sent directly to the patrons.', - }; - C4::Letters::EnqueueLetter( - { letter => $letter, - borrowernumber => undef, - message_transport_type => 'email', - attachments => [$attachment], - to_address => $admin_email_address, - } - ); } + # Generate the content of the csv with headers + my $content = join(";", qw(title name surname address1 address2 zipcode city email itemcount itemsinfo due_date issue_date)) . "\n"; + $content .= join( "\n", @output_chunks ); + + my $attachment = { + filename => defined $csvfilename ? 'attachment.csv' : 'attachment.txt', + type => 'text/plain', + content => $content, + }; + + my $letter = { + title => 'Overdue Notices', + content => 'These messages were not sent directly to the patrons.', + }; + C4::Letters::EnqueueLetter( + { letter => $letter, + borrowernumber => undef, + message_transport_type => 'email', + attachments => [$attachment], + to_address => $admin_email_address, + } + ); } } if ($csvfilename) { - # note that we're not testing on $csv_fh to prevent closing # STDOUT. close $csv_fh; } +if ( defined $htmlfilename ) { + print $html_fh "\n"; + print $html_fh "\n"; + close $html_fh; +} + =head1 INTERNAL METHODS These methods are internal to the operation of overdue_notices.pl. @@ -576,32 +642,52 @@ substituted keys and values. =cut -sub parse_letter { +sub parse_letter { # FIXME: this code should probably be moved to C4::Letters:parseletter my $params = shift; foreach my $required (qw( letter borrowernumber )) { return unless exists $params->{$required}; } + my $todaysdate = C4::Dates->new()->output("syspref"); + $params->{'letter'}->{title} =~ s/<>/$todaysdate/g; + $params->{'letter'}->{content} =~ s/<>/$todaysdate/g; + if ( $params->{'substitute'} ) { while ( my ( $key, $replacedby ) = each %{ $params->{'substitute'} } ) { my $replacefield = "<<$key>>"; - $params->{'letter'}->{title} =~ s/$replacefield/$replacedby/g; $params->{'letter'}->{content} =~ s/$replacefield/$replacedby/g; } } - C4::Letters::parseletter( $params->{'letter'}, 'borrowers', $params->{'borrowernumber'} ); + $params->{'letter'} = C4::Letters::parseletter( $params->{'letter'}, 'borrowers', $params->{'borrowernumber'} ); if ( $params->{'branchcode'} ) { - C4::Letters::parseletter( $params->{'letter'}, 'branches', $params->{'branchcode'} ); + $params->{'letter'} = C4::Letters::parseletter( $params->{'letter'}, 'branches', $params->{'branchcode'} ); } - if ( $params->{'biblionumber'} ) { - C4::Letters::parseletter( $params->{'letter'}, 'biblio', $params->{'biblionumber'} ); - C4::Letters::parseletter( $params->{'letter'}, 'biblioitems', $params->{'biblionumber'} ); - } + if ( $params->{'items'} ) { + my $item_format = ''; + PROCESS_ITEMS: + while (scalar(@{$params->{'items'}}) > 0) { + my $item = shift @{$params->{'items'}}; + my $fine = GetFine($item->{'itemnumber'}, $params->{'borrowernumber'}); + if (!$item_format) { + $params->{'letter'}->{'content'} =~ m/(.*<\/item>)/; + $item_format = $1; + } + if ($params->{'letter'}->{'content'} =~ m/(.*)<\/fine>/) { # process any fine tags... + my $formatted_fine = currency_format("$1", "$fine", FMT_SYMBOL); + $params->{'letter'}->{'content'} =~ s/.*<\/fine>/$formatted_fine/; + } + $params->{'letter'} = C4::Letters::parseletter( $params->{'letter'}, 'biblio', $item->{'biblionumber'} ); + $params->{'letter'} = C4::Letters::parseletter( $params->{'letter'}, 'biblioitems', $item->{'biblionumber'} ); + $params->{'letter'} = C4::Letters::parseletter( $params->{'letter'}, 'items', $item->{'itemnumber'} ); + $params->{'letter'}->{'content'} =~ s/(.*<\/item>)/$1\n$item_format/ if scalar(@{$params->{'items'}} > 0); + } + } + $params->{'letter'}->{'content'} =~ s/<\/{0,1}?item>//g; # strip all remaining item tags... return $params->{'letter'}; } @@ -641,6 +727,10 @@ sub prepare_letter_for_printing { } else { $verbose and warn 'combine failed on argument: ' . $csv->error_input; } + } elsif ( exists $params->{'outputformat'} && $params->{'outputformat'} eq 'html' ) { + $return = "
\n";
+      $return .= "$params->{'letter'}->{'content'}\n";
+      $return .= "\n
\n"; } else { $return .= "$params->{'letter'}->{'content'}\n";