=head1 SYNOPSIS
-overdue_notices.pl [ -n ] [ -library <branchcode> ] [ -library <branchcode>...] [ -max <number of days> ] [ -csv [ <filename> ] ] [ -itemscontent <field list> ]
+overdue_notices.pl
+ [ -n ][ -library <branchcode> ][ -library <branchcode> ... ]
+ [ -max <number of days> ][ -csv [<filename>] ][ -itemscontent <field list> ]
+ [ -email <email_type> ... ]
Options:
-help brief help message
-itemscontent <list of fields> item information in templates
-borcat <categorycode> category code that must be included
-borcatout <categorycode> category code that must be excluded
+ -email <email_type> type of email that will be used. Can be 'email', 'emailpro' or 'B_email'. Repeatable.
=head1 OPTIONS
range of the currently processing notice.
Choose list-all to include all overdue items in the list (limited by B<-max> setting).
+=item B<-date>
+
+use it in order to send overdues on a specific date and not Now.
+
+=item B<-email>
+
+Allows to specify which type of email will be used. Can be email, emailpro or B_email. Repeatable.
+
=back
=head1 DESCRIPTION
my $nomail = 0;
my $MAX = 90;
my @branchcodes; # Branch(es) passed as parameter
+my @emails_to_use; # Emails to use for messaging
+my @emails; # Emails given in command-line parameters
my $csvfilename;
my $htmlfilename;
my $triggered = 0;
my $itemscontent = join( ',', qw( date_due title barcode author itemnumber ) );
my @myborcat;
my @myborcatout;
+my $date;
GetOptions(
'help|?' => \$help,
'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.
+ 'html:s' => \$htmlfilename, # this optional argument gets '' if not supplied.
'itemscontent=s' => \$itemscontent,
- 'list-all' => \$listall,
- 't|triggered' => \$triggered,
- 'borcat=s' => \@myborcat,
- 'borcatout=s' => \@myborcatout,
+ 'list-all' => \$listall,
+ 't|triggered' => \$triggered,
+ 'date' => \$date,
+ 'borcat=s' => \@myborcat,
+ 'borcatout=s' => \@myborcatout,
+ 'email=s' => \@emails,
) or pod2usage(2);
pod2usage(1) if $help;
pod2usage( -verbose => 2 ) if $man;
}
}
+if ($date){
+ $date=$dbh->quote($date);
+}
+else {
+ $date="NOW()";
+}
+
# these are the fields that will be substituted into <<item.content>>
my @item_content_fields = split( /,/, $itemscontent );
-binmode( STDOUT, ":utf8" );
+binmode( STDOUT, ':encoding(UTF-8)' );
our $csv; # the Text::CSV_XS object
if ( $htmlfilename eq '' ) {
$html_fh = *STDOUT;
} else {
- my $today = C4::Dates->new();
- open $html_fh, ">",File::Spec->catdir ($htmlfilename,"notices-".$today->output('iso').".html");
+ my $today = DateTime->now(time_zone => C4::Context->tz );
+ open $html_fh, ">",File::Spec->catdir ($htmlfilename,"notices-".$today->ymd().".html");
}
print $html_fh "<html>\n";
$verbose and warn sprintf "branchcode : '%s' using %s\n", $branchcode, $admin_email_address;
- my $sth2 = $dbh->prepare( <<'END_SQL' );
-SELECT biblio.*, items.*, issues.*, biblioitems.itemtype, TO_DAYS(NOW())-TO_DAYS(date_due) AS days_overdue
+ my $sth2 = $dbh->prepare( <<"END_SQL" );
+SELECT biblio.*, items.*, issues.*, biblioitems.itemtype, TO_DAYS($date)-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 ?
+ AND TO_DAYS($date)-TO_DAYS(date_due) BETWEEN ? and ?
END_SQL
my $query = "SELECT * FROM overduerules WHERE delay1 IS NOT NULL AND branchcode = ? ";
# this text contains fields that are replaced by their value. Those fields must be written between brackets
# The following fields are available :
# itemcount is interpreted here as the number of items in the overdue range defined by the current notice or all overdues < max if(-list-all).
- # <date> <itemcount> <firstname> <lastname> <address1> <address2> <address3> <city> <postcode>
+ # <date> <itemcount> <firstname> <lastname> <address1> <address2> <address3> <city> <postcode> <country>
my $borrower_sql = <<'END_SQL';
-SELECT distinct(issues.borrowernumber), firstname, surname, address, address2, city, zipcode, country, email
+SELECT distinct(issues.borrowernumber), firstname, surname, address, address2, city, zipcode, country, email, emailpro, B_email
FROM issues,borrowers,categories
WHERE issues.borrowernumber=borrowers.borrowernumber
AND borrowers.categorycode=categories.categorycode
}
$borrower_sql .= ' AND categories.overduenoticerequired=1 ';
if($triggered) {
- $borrower_sql .= ' AND TO_DAYS(NOW())-TO_DAYS(date_due) = ?';
+ $borrower_sql .= " AND TO_DAYS($date)-TO_DAYS(date_due) = ?";
push @borrower_parameters, $mindays;
} else {
- $borrower_sql .= ' AND TO_DAYS(NOW())-TO_DAYS(date_due) BETWEEN ? and ? ' ;
+ $borrower_sql .= " AND TO_DAYS($date)-TO_DAYS(date_due) BETWEEN ? and ? " ;
push @borrower_parameters, $mindays, $maxdays;
}
$sth->execute(@borrower_parameters);
$verbose and warn $borrower_sql . "\n $branchcode | " . $overdue_rules->{'categorycode'} . "\n ($mindays, $maxdays)\nreturns " . $sth->rows . " rows";
- while ( my ( $borrowernumber, $firstname, $lastname,
- $address1, $address2, $city, $postcode, $country, $email
- ) = $sth->fetchrow )
- {
- $verbose and warn "borrower $firstname, $lastname ($borrowernumber) has items triggering level $i.";
-
- my $letter = C4::Letters::getletter( 'circulation', $overdue_rules->{"letter$i"} );
+ while ( my $data = $sth->fetchrow_hashref ) {
+ my $borrowernumber = $data->{'borrowernumber'};
+ my $borr =
+ $data->{'firstname'} . ', '
+ . $data->{'surname'} . ' ('
+ . $borrowernumber . ')';
+ $verbose
+ and warn "borrower $borr has items triggering level $i.";
+
+ @emails_to_use = ();
+ my $notice_email =
+ C4::Members::GetNoticeEmailAddress($borrowernumber);
+ unless ($nomail) {
+ if (@emails) {
+ foreach (@emails) {
+ push @emails_to_use, $data->{$_} if ( $data->{$_} );
+ }
+ }
+ else {
+ push @emails_to_use, $notice_email if ($notice_email);
+ }
+ }
+
+ my $letter = C4::Letters::getletter( 'circulation', $overdue_rules->{"letter$i"}, $branchcode );
unless ($letter) {
$verbose and warn "Message '$overdue_rules->{letter$i}' content not found";
-
+
# might as well skip while PERIOD, no other borrowers are going to work.
# FIXME : Does this mean a letter must be defined in order to trigger a debar ?
next PERIOD;
#action taken is debarring
C4::Members::DebarMember($borrowernumber, '9999-12-31');
- $verbose and warn "debarring $borrowernumber $firstname $lastname\n";
+ $verbose and warn "debarring $borr\n";
}
my @params = ($listall ? ( $borrowernumber , 1 , $MAX ) : ( $borrowernumber, $mindays, $maxdays ));
$verbose and warn "STH2 PARAMS: borrowernumber = $borrowernumber, mindays = $mindays, maxdays = $maxdays";
my $titles = "";
my @items = ();
- my $i = 0;
+ my $j = 0;
my $exceededPrintNoticesMaxLines = 0;
while ( my $item_info = $sth2->fetchrow_hashref() ) {
- if ( ( !$email || $nomail ) && $PrintNoticesMaxLines && $i >= $PrintNoticesMaxLines ) {
+ if ( ( scalar(@emails_to_use) == 0 || $nomail ) && $PrintNoticesMaxLines && $j >= $PrintNoticesMaxLines ) {
$exceededPrintNoticesMaxLines = 1;
last;
}
- $i++;
+ $j++;
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'} };
+ push @items, $item_info;
}
$sth2->finish;
+
$letter = parse_letter(
- { letter => $letter,
+ { letter_code => $overdue_rules->{"letter$i"},
borrowernumber => $borrowernumber,
branchcode => $branchcode,
items => \@items,
}
}
);
+ unless ($letter) {
+ $verbose and warn "Message '$overdue_rules->{letter$i}' content not found";
+
+ # might as well skip while PERIOD, no other borrowers are going to work.
+ # FIXME : Does this mean a letter must be defined in order to trigger a debar ?
+ next PERIOD;
+ }
if ( $exceededPrintNoticesMaxLines ) {
$letter->{'content'} .= "List too long for form; please check your account online for a complete list of your overdue items.";
}
$letter->{'content'} =~ s/\<[^<>]*?\>//g; # Now that we've warned about them, remove them.
$letter->{'content'} =~ s/\<[^<>]*?\>//g; # 2nd pass for the double nesting.
-
- if ($nomail) {
-
+
+ if ( !$nomail && scalar @emails_to_use ) {
+ C4::Letters::EnqueueLetter(
+ { letter => $letter,
+ borrowernumber => $borrowernumber,
+ message_transport_type => 'email',
+ from_address => $admin_email_address,
+ to_address => join(',', @emails_to_use),
+ }
+ );
+ } else {
+ # if not sent by email then print
push @output_chunks,
prepare_letter_for_printing(
{ letter => $letter,
borrowernumber => $borrowernumber,
- firstname => $firstname,
- lastname => $lastname,
- address1 => $address1,
- address2 => $address2,
- city => $city,
- postcode => $postcode,
- email => $email,
+ firstname => $data->{'firstname'},
+ lastname => $data->{'surname'},
+ address1 => $data->{'address'},
+ address2 => $data->{'address2'},
+ city => $data->{'city'},
+ postcode => $data->{'zipcode'},
+ country => $data->{'country'},
+ email => $notice_email,
itemcount => $itemcount,
titles => $titles,
outputformat => defined $csvfilename ? 'csv' : defined $htmlfilename ? 'html' : '',
}
);
- } else {
- if ($email) {
- C4::Letters::EnqueueLetter(
- { letter => $letter,
- borrowernumber => $borrowernumber,
- message_transport_type => 'email',
- from_address => $admin_email_address,
- }
- );
- } else {
-
- # If we don't have an email address for this patron, send it to the admin to deal with.
- push @output_chunks,
- prepare_letter_for_printing(
- { letter => $letter,
- borrowernumber => $borrowernumber,
- firstname => $firstname,
- lastname => $lastname,
- address1 => $address1,
- address2 => $address2,
- city => $city,
- postcode => $postcode,
- email => $email,
- itemcount => $itemcount,
- titles => $titles,
- outputformat => defined $csvfilename ? 'csv' : defined $htmlfilename ? 'html' : '',
- }
- );
- }
}
}
$sth->finish;
print @output_chunks;
}
# 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";
+ my $content = join(";", qw(title name surname address1 address2 zipcode city country email itemcount itemsinfo due_date issue_date)) . "\n";
$content .= join( "\n", @output_chunks );
my $attachment = {
=cut
-sub parse_letter { # FIXME: this code should probably be moved to C4::Letters:parseletter
+sub parse_letter {
my $params = shift;
- foreach my $required (qw( letter borrowernumber )) {
- return unless exists $params->{$required};
+ foreach my $required (qw( letter_code borrowernumber )) {
+ return unless ( exists $params->{$required} && $params->{$required} );
}
- my $todaysdate = C4::Dates->new()->output("syspref");
- $params->{'letter'}->{title} =~ s/<<today>>/$todaysdate/g;
- $params->{'letter'}->{content} =~ s/<<today>>/$todaysdate/g;
+ my $substitute = $params->{'substitute'} || {};
+ $substitute->{today} ||= C4::Dates->new()->output("syspref");
- 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;
- }
+ my %tables = ( 'borrowers' => $params->{'borrowernumber'} );
+ if ( my $p = $params->{'branchcode'} ) {
+ $tables{'branches'} = $p;
}
- $params->{'letter'} = C4::Letters::parseletter( $params->{'letter'}, 'borrowers', $params->{'borrowernumber'} );
-
- if ( $params->{'branchcode'} ) {
- $params->{'letter'} = C4::Letters::parseletter( $params->{'letter'}, 'branches', $params->{'branchcode'} );
+ my $currency_format;
+ if ( defined $params->{'letter'}->{'content'}
+ and $params->{'letter'}->{'content'} =~ m/<fine>(.*)<\/fine>/o )
+ { # process any fine tags...
+ $currency_format = $1;
+ $params->{'letter'}->{'content'} =~ s/<fine>.*<\/fine>/<<item.fine>>/o;
}
- if ( $params->{'items'} ) {
+ my @item_tables;
+ if ( my $i = $params->{'items'} ) {
my $item_format = '';
- PROCESS_ITEMS:
- while (scalar(@{$params->{'items'}}) > 0) {
- my $item = shift @{$params->{'items'}};
+ foreach my $item (@$i) {
my $fine = GetFine($item->{'itemnumber'}, $params->{'borrowernumber'});
- if (!$item_format) {
+ if ( !$item_format and defined $params->{'letter'}->{'content'} ) {
$params->{'letter'}->{'content'} =~ m/(<item>.*<\/item>)/;
$item_format = $1;
}
- if ($params->{'letter'}->{'content'} =~ m/<fine>(.*)<\/fine>/) { # process any fine tags...
- my $formatted_fine = currency_format("$1", "$fine", FMT_SYMBOL);
- $params->{'letter'}->{'content'} =~ s/<fine>.*<\/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'} = C4::Letters::parseletter( $params->{'letter'}, 'issues', $item->{'itemnumber'} );
- $params->{'letter'}->{'content'} =~ s/(<item>.*<\/item>)/$1\n$item_format/ if scalar(@{$params->{'items'}} > 0);
+ $item->{'fine'} = currency_format($currency_format, "$fine", FMT_SYMBOL)
+ if $currency_format;
+
+ push @item_tables, {
+ 'biblio' => $item->{'biblionumber'},
+ 'biblioitems' => $item->{'biblionumber'},
+ 'items' => $item,
+ 'issues' => $item->{'itemnumber'},
+ };
}
}
- $params->{'letter'}->{'content'} =~ s/<\/{0,1}?item>//g; # strip all remaining item tags...
- return $params->{'letter'};
+
+ return C4::Letters::GetPreparedLetter (
+ module => 'circulation',
+ letter_code => $params->{'letter_code'},
+ branchcode => $params->{'branchcode'},
+ tables => \%tables,
+ substitute => $substitute,
+ repeat => { item => \@item_tables },
+ );
}
=head2 prepare_letter_for_printing
if ( exists $params->{'outputformat'} && $params->{'outputformat'} eq 'csv' ) {
if ($csv->combine(
$params->{'firstname'}, $params->{'lastname'}, $params->{'address1'}, $params->{'address2'}, $params->{'postcode'},
- $params->{'city'}, $params->{'email'}, $params->{'itemcount'}, $params->{'titles'}
+ $params->{'city'}, $params->{'country'}, $params->{'email'}, $params->{'itemcount'}, $params->{'titles'}
)
) {
return $csv->string, "\n";