|
Lines 108-114
any CSV files. Defaults to 90 to match F<longoverdues.pl>.
Link Here
|
| 108 |
=item B<--library> |
108 |
=item B<--library> |
| 109 |
|
109 |
|
| 110 |
select overdues for one specific library. Use the value in the |
110 |
select overdues for one specific library. Use the value in the |
| 111 |
branches.branchcode table. This option can be repeated in order |
111 |
branches.branchcode table. This option can be repeated in order |
| 112 |
to select overdues for a group of libraries. |
112 |
to select overdues for a group of libraries. |
| 113 |
|
113 |
|
| 114 |
=item B<--csv> |
114 |
=item B<--csv> |
|
Lines 142-147
defaults to due date,title,barcode,author
Link Here
|
| 142 |
Other possible values come from fields in the biblios, items and |
142 |
Other possible values come from fields in the biblios, items and |
| 143 |
issues tables. |
143 |
issues tables. |
| 144 |
|
144 |
|
|
|
145 |
=item B<--itemtypes> |
| 146 |
|
| 147 |
Repeatable field, that permits to select only some item types. |
| 148 |
|
| 149 |
=item B<--itemtypesout> |
| 150 |
|
| 151 |
Repeatable field, that permits to exclude some item types. |
| 152 |
|
| 145 |
=item B<--borcat> |
153 |
=item B<--borcat> |
| 146 |
|
154 |
|
| 147 |
Repeatable field, that permits to select only some patron categories. |
155 |
Repeatable field, that permits to select only some patron categories. |
|
Lines 152-164
Repeatable field, that permits to exclude some patron categories.
Link Here
|
| 152 |
|
160 |
|
| 153 |
=item B<-t> | B<--triggered> |
161 |
=item B<-t> | B<--triggered> |
| 154 |
|
162 |
|
| 155 |
This option causes a notice to be generated if and only if |
163 |
This option causes a notice to be generated if and only if |
| 156 |
an item is overdue by the number of days defined in a notice trigger. |
164 |
an item is overdue by the number of days defined in a notice trigger. |
| 157 |
|
165 |
|
| 158 |
By default, a notice is sent each time the script runs, which is suitable for |
166 |
By default, a notice is sent each time the script runs, which is suitable for |
| 159 |
less frequent run cron script, but requires syncing notice triggers with |
167 |
less frequent run cron script, but requires syncing notice triggers with |
| 160 |
the cron schedule to ensure proper behavior. |
168 |
the cron schedule to ensure proper behavior. |
| 161 |
Add the --triggered option for daily cron, at the risk of no notice |
169 |
Add the --triggered option for daily cron, at the risk of no notice |
| 162 |
being generated if the cron fails to run on time. |
170 |
being generated if the cron fails to run on time. |
| 163 |
|
171 |
|
| 164 |
=item B<--test> |
172 |
=item B<--test> |
|
Lines 170-176
for debugging configuration.
Link Here
|
| 170 |
|
178 |
|
| 171 |
=item B<--list-all> |
179 |
=item B<--list-all> |
| 172 |
|
180 |
|
| 173 |
Default items.content lists only those items that fall in the |
181 |
Default items.content lists only those items that fall in the |
| 174 |
range of the currently processing notice. |
182 |
range of the currently processing notice. |
| 175 |
Choose --list-all to include all overdue items in the list (limited by B<--max> setting). |
183 |
Choose --list-all to include all overdue items in the list (limited by B<--max> setting). |
| 176 |
|
184 |
|
|
Lines 307-312
my $text_filename;
Link Here
|
| 307 |
my $triggered = 0; |
315 |
my $triggered = 0; |
| 308 |
my $listall = 0; |
316 |
my $listall = 0; |
| 309 |
my $itemscontent = join( ',', qw( date_due title barcode author itemnumber ) ); |
317 |
my $itemscontent = join( ',', qw( date_due title barcode author itemnumber ) ); |
|
|
318 |
my @myitemtypes; |
| 319 |
my @myitemtypesout; |
| 310 |
my @myborcat; |
320 |
my @myborcat; |
| 311 |
my @myborcatout; |
321 |
my @myborcatout; |
| 312 |
my ( $date_input, $today ); |
322 |
my ( $date_input, $today ); |
|
Lines 320-329
GetOptions(
Link Here
|
| 320 |
'n|nomail' => \$nomail, |
330 |
'n|nomail' => \$nomail, |
| 321 |
'max=s' => \$MAX, |
331 |
'max=s' => \$MAX, |
| 322 |
'library=s' => \@branchcodes, |
332 |
'library=s' => \@branchcodes, |
| 323 |
'csv:s' => \$csvfilename, # this optional argument gets '' if not supplied. |
333 |
'csv:s' => \$csvfilename, # this optional argument gets '' if not supplied. |
| 324 |
'html:s' => \$htmlfilename, # this optional argument gets '' if not supplied. |
334 |
'html:s' => \$htmlfilename, # this optional argument gets '' if not supplied. |
| 325 |
'text:s' => \$text_filename, # this optional argument gets '' if not supplied. |
335 |
'text:s' => \$text_filename, # this optional argument gets '' if not supplied. |
| 326 |
'itemscontent=s' => \$itemscontent, |
336 |
'itemscontent=s' => \$itemscontent, |
|
|
337 |
'itemtypes=s' => \@myitemtypes, |
| 338 |
'itemtypeouts=s' => \@myitemtypesout, |
| 327 |
'list-all' => \$listall, |
339 |
'list-all' => \$listall, |
| 328 |
't|triggered' => \$triggered, |
340 |
't|triggered' => \$triggered, |
| 329 |
'test' => \$test_mode, |
341 |
'test' => \$test_mode, |
|
Lines 333-341
GetOptions(
Link Here
|
| 333 |
'email=s' => \@emails, |
345 |
'email=s' => \@emails, |
| 334 |
'frombranch=s' => \$frombranch, |
346 |
'frombranch=s' => \$frombranch, |
| 335 |
) or pod2usage(2); |
347 |
) or pod2usage(2); |
| 336 |
pod2usage(1) if $help; |
348 |
pod2usage(1) if $help; |
| 337 |
pod2usage( -verbose => 2 ) if $man; |
349 |
pod2usage( -verbose => 2 ) if $man; |
| 338 |
cronlogaction({ info => $command_line_options }); |
350 |
cronlogaction( { info => $command_line_options } ); |
| 339 |
|
351 |
|
| 340 |
if ( defined $csvfilename && $csvfilename =~ /^-/ ) { |
352 |
if ( defined $csvfilename && $csvfilename =~ /^-/ ) { |
| 341 |
warn qq(using "$csvfilename" as filename, that seems odd); |
353 |
warn qq(using "$csvfilename" as filename, that seems odd); |
|
Lines 367-385
if ($branchcount) {
Link Here
|
| 367 |
|
379 |
|
| 368 |
if (@branchcodes) { |
380 |
if (@branchcodes) { |
| 369 |
$verbose and warn "$branchcodes_word @branchcodes passed on parameter\n"; |
381 |
$verbose and warn "$branchcodes_word @branchcodes passed on parameter\n"; |
| 370 |
|
382 |
|
| 371 |
# Getting libraries which have overdue rules |
383 |
# Getting libraries which have overdue rules |
| 372 |
my %seen = map { $_ => 1 } @branchcodes; |
384 |
my %seen = map { $_ => 1 } @branchcodes; |
| 373 |
@branches = grep { $seen{$_} } @overduebranches; |
385 |
@branches = grep { $seen{$_} } @overduebranches; |
| 374 |
|
386 |
|
| 375 |
|
387 |
|
| 376 |
if (@branches) { |
388 |
if (@branches) { |
| 377 |
|
389 |
|
| 378 |
my $branch_word = scalar @branches > 1 ? 'branches' : 'branch'; |
390 |
my $branch_word = scalar @branches > 1 ? 'branches' : 'branch'; |
| 379 |
$verbose and warn "$branch_word @branches have overdue rules\n"; |
391 |
$verbose and warn "$branch_word @branches have overdue rules\n"; |
| 380 |
|
392 |
|
| 381 |
} else { |
393 |
} else { |
| 382 |
|
394 |
|
| 383 |
$verbose and warn "No active overduerules for $branchcodes_word '@branchcodes'\n"; |
395 |
$verbose and warn "No active overduerules for $branchcodes_word '@branchcodes'\n"; |
| 384 |
( scalar grep { '' eq $_ } @branches ) |
396 |
( scalar grep { '' eq $_ } @branches ) |
| 385 |
or die "No active overduerules for DEFAULT either!"; |
397 |
or die "No active overduerules for DEFAULT either!"; |
|
Lines 437-443
if ( defined $htmlfilename ) {
Link Here
|
| 437 |
my $today = dt_from_string(); |
449 |
my $today = dt_from_string(); |
| 438 |
open $fh, ">:encoding(UTF-8)",File::Spec->catdir ($htmlfilename,"notices-".$today->ymd().".html"); |
450 |
open $fh, ">:encoding(UTF-8)",File::Spec->catdir ($htmlfilename,"notices-".$today->ymd().".html"); |
| 439 |
} |
451 |
} |
| 440 |
|
452 |
|
| 441 |
print $fh _get_html_start(); |
453 |
print $fh _get_html_start(); |
| 442 |
} |
454 |
} |
| 443 |
elsif ( defined $text_filename ) { |
455 |
elsif ( defined $text_filename ) { |
|
Lines 498-547
END_SQL
Link Here
|
| 498 |
@categories = Koha::Patron::Categories->search()->get_column('categorycode'); |
510 |
@categories = Koha::Patron::Categories->search()->get_column('categorycode'); |
| 499 |
} |
511 |
} |
| 500 |
|
512 |
|
|
|
513 |
my @itemtypes; |
| 514 |
if (@myitemtypes) { |
| 515 |
@itemtypes = @myitemtypes; |
| 516 |
} elsif (@myitemtypesout) { |
| 517 |
@itemtypes = |
| 518 |
Koha::ItemTypes->search( { itemtype => { 'not_in' => \@myitemtypesout } } )->get_column('itemtype'); |
| 519 |
} else { |
| 520 |
@itemtypes = Koha::ItemTypes->search()->get_column('itemtype'); |
| 521 |
} |
| 522 |
|
| 501 |
for my $category (@categories) { |
523 |
for my $category (@categories) { |
| 502 |
my $i = 0; |
524 |
for my $itemtype (@itemtypes) { |
| 503 |
PERIOD: while (1) { |
525 |
my $i = 0; |
| 504 |
$i++; |
526 |
PERIOD: while (1) { |
| 505 |
my $j = $i + 1; |
527 |
$i++; |
| 506 |
my $overdue_rules = Koha::CirculationRules->get_effective_rules( |
528 |
my $j = $i + 1; |
| 507 |
{ |
529 |
my $overdue_rules = Koha::CirculationRules->get_effective_rules( |
| 508 |
rules => [ |
530 |
{ |
| 509 |
"overdue_$i" . '_delay', "overdue_$i" . '_notice', "overdue_$i" . '_mtt', |
531 |
rules => [ |
| 510 |
"overdue_$i" . '_restrict', "overdue_$j" . '_delay' |
532 |
"overdue_$i" . '_delay', "overdue_$i" . '_notice', "overdue_$i" . '_mtt', |
| 511 |
], |
533 |
"overdue_$i" . '_restrict', "overdue_$j" . '_delay' |
| 512 |
categorycode => $category, |
534 |
], |
| 513 |
branchcode => $branchcode |
535 |
categorycode => $category, |
| 514 |
} |
536 |
branchcode => $branchcode, |
| 515 |
); |
537 |
itemtype => $itemtype, |
| 516 |
$verbose and warn "branch '$branchcode', categorycode = $category pass $i\n"; |
538 |
} |
|
|
539 |
); |
| 540 |
$verbose and warn "branch '$branchcode', categorycode = $category, itemtype = $itemtype, pass $i\n"; |
| 517 |
|
541 |
|
| 518 |
if (!$overdue_rules->{"overdue_$i".'_delay'}) { |
542 |
if ( !defined( $overdue_rules->{ "overdue_$i" . '_delay' } ) ) { |
| 519 |
last PERIOD; |
543 |
last PERIOD; |
| 520 |
} |
544 |
} |
| 521 |
|
545 |
|
| 522 |
my $mindays = |
546 |
my $mindays = |
| 523 |
$overdue_rules->{ "overdue_$i" . '_delay' }; # the notice will be sent after mindays days (grace period) |
547 |
$overdue_rules->{ "overdue_$i" . '_delay' }; # the notice will be sent after mindays days (grace period) |
| 524 |
my $maxdays = ( |
548 |
my $maxdays = ( |
| 525 |
$overdue_rules->{ "overdue_$j" . '_delay' } |
549 |
$overdue_rules->{ "overdue_$j" . '_delay' } |
| 526 |
? $overdue_rules->{ "overdue_$j" . '_delay' } - 1 |
550 |
? $overdue_rules->{ "overdue_$j" . '_delay' } - 1 |
| 527 |
: ($MAX) |
551 |
: ($MAX) |
| 528 |
); # issues being more than maxdays late are managed somewhere else. (borrower probably suspended) |
552 |
); # issues being more than maxdays late are managed somewhere else. (borrower probably suspended) |
| 529 |
|
553 |
|
| 530 |
next unless defined $mindays; |
554 |
next unless defined $mindays; |
| 531 |
|
555 |
|
| 532 |
if ( !$overdue_rules->{"overdue_$i".'_notice'} ) { |
556 |
if ( !$overdue_rules->{"overdue_$i".'_notice'} ) { |
| 533 |
$verbose and warn sprintf "No letter code found for pass %s\n", $i; |
557 |
$verbose and warn sprintf "No letter code found for pass %s\n", $i; |
| 534 |
next PERIOD; |
558 |
next PERIOD; |
| 535 |
} |
559 |
} |
| 536 |
$verbose and warn sprintf "Using letter code '%s' for pass %s\n", $overdue_rules->{"overdue_$i".'_notice'}, $i; |
560 |
$verbose and warn sprintf "Using letter code '%s' for pass %s\n", $overdue_rules->{"overdue_$i".'_notice'}, $i; |
| 537 |
|
561 |
|
| 538 |
# $letter->{'content'} is the text of the mail that is sent. |
562 |
# $letter->{'content'} is the text of the mail that is sent. |
| 539 |
# this text contains fields that are replaced by their value. Those fields must be written between brackets |
563 |
# this text contains fields that are replaced by their value. Those fields must be written between brackets |
| 540 |
# The following fields are available : |
564 |
# The following fields are available : |
| 541 |
# 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). |
565 |
# 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). |
| 542 |
# <date> <itemcount> <firstname> <lastname> <address1> <address2> <address3> <city> <postcode> <country> |
566 |
# <date> <itemcount> <firstname> <lastname> <address1> <address2> <address3> <city> <postcode> <country> |
| 543 |
|
567 |
|
| 544 |
my $borrower_sql = <<"END_SQL"; |
568 |
my $borrower_sql = <<"END_SQL"; |
| 545 |
SELECT issues.borrowernumber, firstname, surname, address, address2, city, zipcode, country, email, emailpro, B_email, smsalertnumber, phone, cardnumber, date_due |
569 |
SELECT issues.borrowernumber, firstname, surname, address, address2, city, zipcode, country, email, emailpro, B_email, smsalertnumber, phone, cardnumber, date_due |
| 546 |
FROM issues,borrowers,categories,items |
570 |
FROM issues,borrowers,categories,items |
| 547 |
WHERE issues.borrowernumber=borrowers.borrowernumber |
571 |
WHERE issues.borrowernumber=borrowers.borrowernumber |
|
Lines 550-806
AND issues.itemnumber = items.itemnumber
Link Here
|
| 550 |
AND items.itemlost = 0 |
574 |
AND items.itemlost = 0 |
| 551 |
AND TO_DAYS($date)-TO_DAYS(issues.date_due) >= 0 |
575 |
AND TO_DAYS($date)-TO_DAYS(issues.date_due) >= 0 |
| 552 |
END_SQL |
576 |
END_SQL |
| 553 |
my @borrower_parameters; |
577 |
my @borrower_parameters; |
| 554 |
if ($branchcode) { |
578 |
if ($branchcode) { |
| 555 |
if($owning_library) { |
579 |
if($owning_library) { |
| 556 |
$borrower_sql .= ' AND items.homebranch=? '; |
580 |
$borrower_sql .= ' AND items.homebranch=? '; |
| 557 |
} else { |
581 |
} else { |
| 558 |
$borrower_sql .= ' AND issues.branchcode=? '; |
582 |
$borrower_sql .= ' AND issues.branchcode=? '; |
| 559 |
} |
|
|
| 560 |
push @borrower_parameters, $branchcode; |
| 561 |
} |
| 562 |
if ( $category ) { |
| 563 |
$borrower_sql .= ' AND borrowers.categorycode=? '; |
| 564 |
push @borrower_parameters, $category; |
| 565 |
} |
583 |
} |
| 566 |
$borrower_sql .= ' AND categories.overduenoticerequired=1 ORDER BY issues.borrowernumber'; |
584 |
push @borrower_parameters, $branchcode; |
| 567 |
|
|
|
| 568 |
# $sth gets borrower info iff at least one overdue item has triggered the overdue action. |
| 569 |
my $sth = $dbh->prepare($borrower_sql); |
| 570 |
$sth->execute(@borrower_parameters); |
| 571 |
|
| 572 |
if ( $verbose > 1 ){ |
| 573 |
warn sprintf "--------Borrower SQL------\n"; |
| 574 |
warn $borrower_sql . "\n $branchcode | " . $category . "\n ($mindays, $maxdays, ". $date_to_run->datetime() .")\n"; |
| 575 |
warn sprintf "--------------------------\n"; |
| 576 |
} |
| 577 |
$verbose and warn sprintf "Found %s borrowers with overdues\n", $sth->rows; |
| 578 |
my $borrowernumber; |
| 579 |
while ( my $data = $sth->fetchrow_hashref ) { |
| 580 |
|
| 581 |
# check the borrower has at least one item that matches |
| 582 |
my $days_between; |
| 583 |
if ( C4::Context->preference('OverdueNoticeCalendar') ) |
| 584 |
{ |
| 585 |
$days_between = |
| 586 |
$calendar->days_between( dt_from_string($data->{date_due}), |
| 587 |
$date_to_run ); |
| 588 |
} |
585 |
} |
| 589 |
else { |
586 |
if ( $category ) { |
| 590 |
$days_between = |
587 |
$borrower_sql .= ' AND borrowers.categorycode=? '; |
| 591 |
$date_to_run->delta_days( dt_from_string($data->{date_due}) ); |
588 |
push @borrower_parameters, $category; |
| 592 |
} |
589 |
} |
| 593 |
$days_between = $days_between->in_units('days'); |
590 |
$borrower_sql .= ' AND categories.overduenoticerequired=1 ORDER BY issues.borrowernumber'; |
| 594 |
if ($triggered) { |
|
|
| 595 |
if ( $mindays != $days_between ) { |
| 596 |
next; |
| 597 |
} |
| 598 |
} |
| 599 |
else { |
| 600 |
unless ( $days_between >= $mindays |
| 601 |
&& $days_between <= $maxdays ) |
| 602 |
{ |
| 603 |
next; |
| 604 |
} |
| 605 |
} |
| 606 |
if (defined $borrowernumber && $borrowernumber eq $data->{'borrowernumber'}){ |
| 607 |
# we have already dealt with this borrower |
| 608 |
$verbose and warn "already dealt with this borrower $borrowernumber"; |
| 609 |
next; |
| 610 |
} |
| 611 |
$borrowernumber = $data->{'borrowernumber'}; |
| 612 |
next if ( $patron_homelibrary && $already_queued{"$borrowernumber$i"} ); |
| 613 |
my $borr = sprintf( "%s%s%s (%s)", |
| 614 |
$data->{'surname'} || '', |
| 615 |
$data->{'firstname'} && $data->{'surname'} ? ', ' : '', |
| 616 |
$data->{'firstname'} || '', |
| 617 |
$borrowernumber ); |
| 618 |
$verbose and warn "borrower $borr has items triggering level $i.\n"; |
| 619 |
|
| 620 |
my $patron = Koha::Patrons->find( $borrowernumber ); |
| 621 |
if ($patron_homelibrary) { |
| 622 |
$branchcode = $patron->branchcode; |
| 623 |
$library = Koha::Libraries->find($branchcode); |
| 624 |
$admin_email_address = $library->from_email_address; |
| 625 |
$branch_email_address = C4::Context->preference('AddressForFailedOverdueNotices') |
| 626 |
|| $library->inbound_email_address; |
| 627 |
} |
| 628 |
@emails_to_use = (); |
| 629 |
my $notice_email = $patron->notice_email_address; |
| 630 |
unless ($nomail) { |
| 631 |
if (@emails) { |
| 632 |
foreach (@emails) { |
| 633 |
push @emails_to_use, $data->{$_} if ( $data->{$_} ); |
| 634 |
} |
| 635 |
} |
| 636 |
else { |
| 637 |
push @emails_to_use, $notice_email if ($notice_email); |
| 638 |
} |
| 639 |
} |
| 640 |
|
| 641 |
my $letter = Koha::Notice::Templates->find_effective_template( |
| 642 |
{ |
| 643 |
module => 'circulation', |
| 644 |
code => $overdue_rules->{"overdue_$i".'_notice'}, |
| 645 |
branchcode => $branchcode, |
| 646 |
lang => $patron->lang |
| 647 |
} |
| 648 |
); |
| 649 |
|
591 |
|
| 650 |
unless ($letter) { |
592 |
# $sth gets borrower info iff at least one overdue item has triggered the overdue action. |
| 651 |
$verbose and warn qq|Message '$overdue_rules->{"overdue_$i"."_notice"}' content not found|; |
593 |
my $sth = $dbh->prepare($borrower_sql); |
|
|
594 |
$sth->execute(@borrower_parameters); |
| 652 |
|
595 |
|
| 653 |
# might as well skip while PERIOD, no other borrowers are going to work. |
596 |
if ( $verbose > 1 ){ |
| 654 |
# FIXME : Does this mean a letter must be defined in order to trigger a debar ? |
597 |
warn sprintf "--------Borrower SQL------\n"; |
| 655 |
next PERIOD; |
598 |
warn $borrower_sql . "\n $branchcode | " . $category . "\n ($mindays, $maxdays, ". $date_to_run->datetime() .")\n"; |
| 656 |
} |
599 |
warn sprintf "--------------------------\n"; |
| 657 |
|
|
|
| 658 |
if ( $overdue_rules->{"overdue_$i".'_restrict'} ) { |
| 659 |
|
| 660 |
#action taken is debarring |
| 661 |
AddUniqueDebarment( |
| 662 |
{ |
| 663 |
borrowernumber => $borrowernumber, |
| 664 |
type => 'OVERDUES', |
| 665 |
comment => "OVERDUES_PROCESS " . output_pref( dt_from_string() ), |
| 666 |
} |
| 667 |
) unless $test_mode; |
| 668 |
$verbose and warn "debarring $borr\n"; |
| 669 |
} |
600 |
} |
| 670 |
my @params = $patron_homelibrary ? ($borrowernumber) : ( $borrowernumber, $branchcode ); |
601 |
$verbose and warn sprintf "Found %s borrowers with overdues\n", $sth->rows; |
| 671 |
|
602 |
my $borrowernumber; |
| 672 |
$sth2->execute(@params); |
603 |
while ( my $data = $sth->fetchrow_hashref ) { |
| 673 |
my $itemcount = 0; |
604 |
|
| 674 |
my $titles = ""; |
605 |
# check the borrower has at least one item that matches |
| 675 |
my @items = (); |
606 |
my $days_between; |
| 676 |
|
607 |
if ( C4::Context->preference('OverdueNoticeCalendar') ) |
| 677 |
my $j = 0; |
608 |
{ |
| 678 |
my $exceededPrintNoticesMaxLines = 0; |
|
|
| 679 |
while ( my $item_info = $sth2->fetchrow_hashref() ) { |
| 680 |
if ( C4::Context->preference('OverdueNoticeCalendar') ) { |
| 681 |
$days_between = |
609 |
$days_between = |
| 682 |
$calendar->days_between( |
610 |
$calendar->days_between( dt_from_string($data->{date_due}), |
| 683 |
dt_from_string( $item_info->{date_due} ), $date_to_run ); |
611 |
$date_to_run ); |
| 684 |
} |
612 |
} |
| 685 |
else { |
613 |
else { |
| 686 |
$days_between = |
614 |
$days_between = |
| 687 |
$date_to_run->delta_days( |
615 |
$date_to_run->delta_days( dt_from_string($data->{date_due}) ); |
| 688 |
dt_from_string( $item_info->{date_due} ) ); |
|
|
| 689 |
} |
616 |
} |
| 690 |
$days_between = $days_between->in_units('days'); |
617 |
$days_between = $days_between->in_units('days'); |
| 691 |
if ($listall){ |
618 |
if ($triggered) { |
| 692 |
unless ($days_between >= 1 and $days_between <= $MAX){ |
619 |
if ( $mindays != $days_between ) { |
| 693 |
next; |
620 |
next; |
| 694 |
} |
621 |
} |
| 695 |
} |
622 |
} |
| 696 |
else { |
623 |
else { |
| 697 |
if ($triggered) { |
624 |
unless ( $days_between >= $mindays |
| 698 |
if ( $mindays != $days_between ) { |
625 |
&& $days_between <= $maxdays ) |
| 699 |
next; |
626 |
{ |
|
|
627 |
next; |
| 628 |
} |
| 629 |
} |
| 630 |
if (defined $borrowernumber && $borrowernumber eq $data->{'borrowernumber'}){ |
| 631 |
# we have already dealt with this borrower |
| 632 |
$verbose and warn "already dealt with this borrower $borrowernumber"; |
| 633 |
next; |
| 634 |
} |
| 635 |
$borrowernumber = $data->{'borrowernumber'}; |
| 636 |
next if ( $patron_homelibrary && $already_queued{"$borrowernumber$i"} ); |
| 637 |
my $borr = sprintf( "%s%s%s (%s)", |
| 638 |
$data->{'surname'} || '', |
| 639 |
$data->{'firstname'} && $data->{'surname'} ? ', ' : '', |
| 640 |
$data->{'firstname'} || '', |
| 641 |
$borrowernumber ); |
| 642 |
$verbose and warn "borrower $borr has items triggering level $i.\n"; |
| 643 |
|
| 644 |
my $patron = Koha::Patrons->find( $borrowernumber ); |
| 645 |
if ($patron_homelibrary) { |
| 646 |
$branchcode = $patron->branchcode; |
| 647 |
$library = Koha::Libraries->find($branchcode); |
| 648 |
$admin_email_address = $library->from_email_address; |
| 649 |
$branch_email_address = C4::Context->preference('AddressForFailedOverdueNotices') |
| 650 |
|| $library->inbound_email_address; |
| 651 |
} |
| 652 |
@emails_to_use = (); |
| 653 |
my $notice_email = $patron->notice_email_address; |
| 654 |
unless ($nomail) { |
| 655 |
if (@emails) { |
| 656 |
foreach (@emails) { |
| 657 |
push @emails_to_use, $data->{$_} if ( $data->{$_} ); |
| 700 |
} |
658 |
} |
| 701 |
} |
659 |
} |
| 702 |
else { |
660 |
else { |
| 703 |
unless ( $days_between >= $mindays |
661 |
push @emails_to_use, $notice_email if ($notice_email); |
| 704 |
&& $days_between <= $maxdays ) |
|
|
| 705 |
{ |
| 706 |
next; |
| 707 |
} |
| 708 |
} |
662 |
} |
| 709 |
} |
663 |
} |
| 710 |
|
664 |
|
| 711 |
if ( ( scalar(@emails_to_use) == 0 || $nomail ) && $PrintNoticesMaxLines && $j >= $PrintNoticesMaxLines ) { |
665 |
my $letter = Koha::Notice::Templates->find_effective_template( |
| 712 |
$exceededPrintNoticesMaxLines = 1; |
|
|
| 713 |
last; |
| 714 |
} |
| 715 |
next if $patron_homelibrary and !grep { $seen{ $item_info->{branchcode} } } @branches; |
| 716 |
$j++; |
| 717 |
|
| 718 |
$titles .= C4::Letters::get_item_content( { item => $item_info, item_content_fields => \@item_content_fields, dateonly => 1 } ); |
| 719 |
$itemcount++; |
| 720 |
push @items, $item_info; |
| 721 |
} |
| 722 |
$sth2->finish; |
| 723 |
|
| 724 |
my @message_transport_types = split( /,/, $overdue_rules->{ "overdue_$i" . '_mtt' } ); |
| 725 |
|
| 726 |
my $print_sent = 0; # A print notice is not yet sent for this patron |
| 727 |
for my $mtt ( @message_transport_types ) { |
| 728 |
next if $mtt eq 'itiva'; |
| 729 |
my $effective_mtt = $mtt; |
| 730 |
if ( ($mtt eq 'email' and not scalar @emails_to_use) or ($mtt eq 'sms' and not $data->{smsalertnumber}) ) { |
| 731 |
# email or sms is requested but not exist, do a print. |
| 732 |
$effective_mtt = 'print'; |
| 733 |
} |
| 734 |
splice @items, $PrintNoticesMaxLines if $effective_mtt eq 'print' && $PrintNoticesMaxLines && scalar @items > $PrintNoticesMaxLines; |
| 735 |
#catch the case where we are sending a print to someone with an email |
| 736 |
|
| 737 |
my $letter_exists = Koha::Notice::Templates->find_effective_template( |
| 738 |
{ |
666 |
{ |
| 739 |
module => 'circulation', |
667 |
module => 'circulation', |
| 740 |
code => $overdue_rules->{ "overdue_$i" . '_notice' }, |
668 |
code => $overdue_rules->{"overdue_$i".'_notice'}, |
| 741 |
message_transport_type => $effective_mtt, |
669 |
branchcode => $branchcode, |
| 742 |
branchcode => $branchcode, |
670 |
lang => $patron->lang |
| 743 |
lang => $patron->lang |
|
|
| 744 |
} |
671 |
} |
| 745 |
); |
672 |
); |
| 746 |
|
673 |
|
| 747 |
my $letter = parse_overdues_letter( |
674 |
unless ($letter) { |
| 748 |
{ |
|
|
| 749 |
letter_code => $overdue_rules->{ "overdue_$i" . '_notice' }, |
| 750 |
borrowernumber => $borrowernumber, |
| 751 |
branchcode => $branchcode, |
| 752 |
items => \@items, |
| 753 |
substitute => { # this appears to be a hack to overcome incomplete features in this code. |
| 754 |
bib => $library->branchname, # maybe 'bib' is a typo for 'lib<rary>'? |
| 755 |
'items.content' => $titles, |
| 756 |
'count' => $itemcount, |
| 757 |
}, |
| 758 |
|
| 759 |
# If there is no template defined for the requested letter |
| 760 |
# Fallback on the original type |
| 761 |
message_transport_type => $letter_exists ? $effective_mtt : $mtt, |
| 762 |
} |
| 763 |
); |
| 764 |
unless ( $letter && $letter->{content} ) { |
| 765 |
$verbose and warn qq|Message '$overdue_rules->{"overdue_$i"."_notice"}' content not found|; |
675 |
$verbose and warn qq|Message '$overdue_rules->{"overdue_$i"."_notice"}' content not found|; |
| 766 |
|
676 |
|
| 767 |
# this transport doesn't have a configured notice, so try another |
677 |
# might as well skip while PERIOD, no other borrowers are going to work. |
| 768 |
next; |
678 |
# FIXME : Does this mean a letter must be defined in order to trigger a debar ? |
|
|
679 |
next PERIOD; |
| 769 |
} |
680 |
} |
| 770 |
|
681 |
|
| 771 |
if ( $exceededPrintNoticesMaxLines ) { |
682 |
if ( $overdue_rules->{"overdue_$i".'_restrict'} ) { |
| 772 |
$letter->{'content'} .= "List too long for form; please check your account online for a complete list of your overdue items."; |
683 |
|
|
|
684 |
#action taken is debarring |
| 685 |
AddUniqueDebarment( |
| 686 |
{ |
| 687 |
borrowernumber => $borrowernumber, |
| 688 |
type => 'OVERDUES', |
| 689 |
comment => "OVERDUES_PROCESS " . output_pref( dt_from_string() ), |
| 690 |
} |
| 691 |
) unless $test_mode; |
| 692 |
$verbose and warn "debarring $borr\n"; |
| 773 |
} |
693 |
} |
|
|
694 |
my @params = $patron_homelibrary ? ($borrowernumber) : ( $borrowernumber, $branchcode ); |
| 695 |
|
| 696 |
$sth2->execute(@params); |
| 697 |
my $itemcount = 0; |
| 698 |
my $titles = ""; |
| 699 |
my @items = (); |
| 700 |
|
| 701 |
my $j = 0; |
| 702 |
my $exceededPrintNoticesMaxLines = 0; |
| 703 |
while ( my $item_info = $sth2->fetchrow_hashref() ) { |
| 704 |
if ( C4::Context->preference('OverdueNoticeCalendar') ) { |
| 705 |
$days_between = |
| 706 |
$calendar->days_between( |
| 707 |
dt_from_string( $item_info->{date_due} ), $date_to_run ); |
| 708 |
} |
| 709 |
else { |
| 710 |
$days_between = |
| 711 |
$date_to_run->delta_days( |
| 712 |
dt_from_string( $item_info->{date_due} ) ); |
| 713 |
} |
| 714 |
$days_between = $days_between->in_units('days'); |
| 715 |
if ($listall){ |
| 716 |
unless ($days_between >= 1 and $days_between <= $MAX){ |
| 717 |
next; |
| 718 |
} |
| 719 |
} |
| 720 |
else { |
| 721 |
if ($triggered) { |
| 722 |
if ( $mindays != $days_between ) { |
| 723 |
next; |
| 724 |
} |
| 725 |
} |
| 726 |
else { |
| 727 |
unless ( $days_between >= $mindays |
| 728 |
&& $days_between <= $maxdays ) |
| 729 |
{ |
| 730 |
next; |
| 731 |
} |
| 732 |
} |
| 733 |
} |
| 734 |
|
| 735 |
if ( ( scalar(@emails_to_use) == 0 || $nomail ) && $PrintNoticesMaxLines && $j >= $PrintNoticesMaxLines ) { |
| 736 |
$exceededPrintNoticesMaxLines = 1; |
| 737 |
last; |
| 738 |
} |
| 739 |
next if $patron_homelibrary and !grep { $seen{ $item_info->{branchcode} } } @branches; |
| 740 |
$j++; |
| 774 |
|
741 |
|
| 775 |
my @misses = grep { /./ } map { /^([^>]*)[>]+/; ( $1 || '' ); } split /\</, $letter->{'content'}; |
742 |
$titles .= C4::Letters::get_item_content( { item => $item_info, item_content_fields => \@item_content_fields, dateonly => 1 } ); |
| 776 |
if (@misses) { |
743 |
$itemcount++; |
| 777 |
$verbose and warn "The following terms were not matched and replaced: \n\t" . join "\n\t", @misses; |
744 |
push @items, $item_info; |
| 778 |
} |
745 |
} |
|
|
746 |
$sth2->finish; |
| 779 |
|
747 |
|
| 780 |
if ($nomail) { |
748 |
my @message_transport_types = split( /,/, $overdue_rules->{ "overdue_$i" . '_mtt' } ); |
| 781 |
push @output_chunks, |
749 |
|
| 782 |
prepare_letter_for_printing( |
750 |
my $print_sent = 0; # A print notice is not yet sent for this patron |
| 783 |
{ letter => $letter, |
751 |
for my $mtt ( @message_transport_types ) { |
| 784 |
borrowernumber => $borrowernumber, |
752 |
next if $mtt eq 'itiva'; |
| 785 |
firstname => $data->{'firstname'}, |
753 |
my $effective_mtt = $mtt; |
| 786 |
lastname => $data->{'surname'}, |
|
|
| 787 |
address1 => $data->{'address'}, |
| 788 |
address2 => $data->{'address2'}, |
| 789 |
city => $data->{'city'}, |
| 790 |
phone => $data->{'phone'}, |
| 791 |
cardnumber => $data->{'cardnumber'}, |
| 792 |
branchname => $library->branchname, |
| 793 |
letternumber => $i, |
| 794 |
postcode => $data->{'zipcode'}, |
| 795 |
country => $data->{'country'}, |
| 796 |
email => $notice_email, |
| 797 |
itemcount => $itemcount, |
| 798 |
titles => $titles, |
| 799 |
outputformat => defined $csvfilename ? 'csv' : defined $htmlfilename ? 'html' : defined $text_filename ? 'text' : '', |
| 800 |
} |
| 801 |
); |
| 802 |
} else { |
| 803 |
if ( ($mtt eq 'email' and not scalar @emails_to_use) or ($mtt eq 'sms' and not $data->{smsalertnumber}) ) { |
754 |
if ( ($mtt eq 'email' and not scalar @emails_to_use) or ($mtt eq 'sms' and not $data->{smsalertnumber}) ) { |
|
|
755 |
# email or sms is requested but not exist, do a print. |
| 756 |
$effective_mtt = 'print'; |
| 757 |
} |
| 758 |
splice @items, $PrintNoticesMaxLines if $effective_mtt eq 'print' && $PrintNoticesMaxLines && scalar @items > $PrintNoticesMaxLines; |
| 759 |
#catch the case where we are sending a print to someone with an email |
| 760 |
|
| 761 |
my $letter_exists = Koha::Notice::Templates->find_effective_template( |
| 762 |
{ |
| 763 |
module => 'circulation', |
| 764 |
code => $overdue_rules->{ "overdue_$i" . '_notice' }, |
| 765 |
message_transport_type => $effective_mtt, |
| 766 |
branchcode => $branchcode, |
| 767 |
lang => $patron->lang |
| 768 |
} |
| 769 |
); |
| 770 |
|
| 771 |
my $letter = parse_overdues_letter( |
| 772 |
{ |
| 773 |
letter_code => $overdue_rules->{ "overdue_$i" . '_notice' }, |
| 774 |
borrowernumber => $borrowernumber, |
| 775 |
branchcode => $branchcode, |
| 776 |
items => \@items, |
| 777 |
substitute => { # this appears to be a hack to overcome incomplete features in this code. |
| 778 |
bib => $library->branchname, # maybe 'bib' is a typo for 'lib<rary>'? |
| 779 |
'items.content' => $titles, |
| 780 |
'count' => $itemcount, |
| 781 |
}, |
| 782 |
|
| 783 |
# If there is no template defined for the requested letter |
| 784 |
# Fallback on the original type |
| 785 |
message_transport_type => $letter_exists ? $effective_mtt : $mtt, |
| 786 |
} |
| 787 |
); |
| 788 |
unless ( $letter && $letter->{content} ) { |
| 789 |
$verbose and warn qq|Message '$overdue_rules->{"overdue_$i"."_notice"}' content not found|; |
| 790 |
|
| 791 |
# this transport doesn't have a configured notice, so try another |
| 792 |
next; |
| 793 |
} |
| 794 |
|
| 795 |
if ( $exceededPrintNoticesMaxLines ) { |
| 796 |
$letter->{'content'} .= "List too long for form; please check your account online for a complete list of your overdue items."; |
| 797 |
} |
| 798 |
|
| 799 |
my @misses = grep { /./ } map { /^([^>]*)[>]+/; ( $1 || '' ); } split /\</, $letter->{'content'}; |
| 800 |
if (@misses) { |
| 801 |
$verbose and warn "The following terms were not matched and replaced: \n\t" . join "\n\t", @misses; |
| 802 |
} |
| 803 |
|
| 804 |
if ($nomail) { |
| 804 |
push @output_chunks, |
805 |
push @output_chunks, |
| 805 |
prepare_letter_for_printing( |
806 |
prepare_letter_for_printing( |
| 806 |
{ letter => $letter, |
807 |
{ letter => $letter, |
|
Lines 810-815
END_SQL
Link Here
|
| 810 |
address1 => $data->{'address'}, |
811 |
address1 => $data->{'address'}, |
| 811 |
address2 => $data->{'address2'}, |
812 |
address2 => $data->{'address2'}, |
| 812 |
city => $data->{'city'}, |
813 |
city => $data->{'city'}, |
|
|
814 |
phone => $data->{'phone'}, |
| 815 |
cardnumber => $data->{'cardnumber'}, |
| 816 |
branchname => $library->branchname, |
| 817 |
letternumber => $i, |
| 813 |
postcode => $data->{'zipcode'}, |
818 |
postcode => $data->{'zipcode'}, |
| 814 |
country => $data->{'country'}, |
819 |
country => $data->{'country'}, |
| 815 |
email => $notice_email, |
820 |
email => $notice_email, |
|
Lines 818-856
END_SQL
Link Here
|
| 818 |
outputformat => defined $csvfilename ? 'csv' : defined $htmlfilename ? 'html' : defined $text_filename ? 'text' : '', |
823 |
outputformat => defined $csvfilename ? 'csv' : defined $htmlfilename ? 'html' : defined $text_filename ? 'text' : '', |
| 819 |
} |
824 |
} |
| 820 |
); |
825 |
); |
| 821 |
} |
826 |
} else { |
| 822 |
unless ( $effective_mtt eq 'print' and $print_sent == 1 ) { |
827 |
if ( ($mtt eq 'email' and not scalar @emails_to_use) or ($mtt eq 'sms' and not $data->{smsalertnumber}) ) { |
| 823 |
# Just sent a print if not already done. |
828 |
push @output_chunks, |
| 824 |
C4::Letters::EnqueueLetter( |
829 |
prepare_letter_for_printing( |
| 825 |
{ letter => $letter, |
830 |
{ letter => $letter, |
| 826 |
borrowernumber => $borrowernumber, |
831 |
borrowernumber => $borrowernumber, |
| 827 |
message_transport_type => $effective_mtt, |
832 |
firstname => $data->{'firstname'}, |
| 828 |
from_address => $admin_email_address, |
833 |
lastname => $data->{'surname'}, |
| 829 |
to_address => join(',', @emails_to_use), |
834 |
address1 => $data->{'address'}, |
| 830 |
reply_address => $library->inbound_email_address, |
835 |
address2 => $data->{'address2'}, |
| 831 |
} |
836 |
city => $data->{'city'}, |
| 832 |
) unless $test_mode; |
837 |
postcode => $data->{'zipcode'}, |
| 833 |
# A print notice should be sent only once per overdue level. |
838 |
country => $data->{'country'}, |
| 834 |
# Without this check, a print could be sent twice or more if the library checks sms and email and print and the patron has no email or sms number. |
839 |
email => $notice_email, |
| 835 |
$print_sent = 1 if $effective_mtt eq 'print'; |
840 |
itemcount => $itemcount, |
|
|
841 |
titles => $titles, |
| 842 |
outputformat => defined $csvfilename ? 'csv' : defined $htmlfilename ? 'html' : defined $text_filename ? 'text' : '', |
| 843 |
} |
| 844 |
); |
| 845 |
} |
| 846 |
unless ( $effective_mtt eq 'print' and $print_sent == 1 ) { |
| 847 |
# Just sent a print if not already done. |
| 848 |
C4::Letters::EnqueueLetter( |
| 849 |
{ letter => $letter, |
| 850 |
borrowernumber => $borrowernumber, |
| 851 |
message_transport_type => $effective_mtt, |
| 852 |
from_address => $admin_email_address, |
| 853 |
to_address => join(',', @emails_to_use), |
| 854 |
reply_address => $library->inbound_email_address, |
| 855 |
} |
| 856 |
) unless $test_mode; |
| 857 |
# A print notice should be sent only once per overdue level. |
| 858 |
# Without this check, a print could be sent twice or more if the library checks sms and email and print and the patron has no email or sms number. |
| 859 |
$print_sent = 1 if $effective_mtt eq 'print'; |
| 860 |
} |
| 836 |
} |
861 |
} |
| 837 |
} |
862 |
} |
|
|
863 |
$already_queued{"$borrowernumber$i"} = 1; |
| 838 |
} |
864 |
} |
| 839 |
$already_queued{"$borrowernumber$i"} = 1; |
865 |
$sth->finish; |
| 840 |
} |
866 |
} |
| 841 |
$sth->finish; |
|
|
| 842 |
} |
867 |
} |
| 843 |
} |
868 |
} |
| 844 |
|
869 |
|
| 845 |
if (@output_chunks) { |
870 |
if (@output_chunks) { |
| 846 |
if ( defined $csvfilename ) { |
871 |
if ( defined $csvfilename ) { |
| 847 |
print $csv_fh @output_chunks; |
872 |
print $csv_fh @output_chunks; |
| 848 |
} |
873 |
} |
| 849 |
elsif ( defined $htmlfilename ) { |
874 |
elsif ( defined $htmlfilename ) { |
| 850 |
print $fh @output_chunks; |
875 |
print $fh @output_chunks; |
| 851 |
} |
876 |
} |
| 852 |
elsif ( defined $text_filename ) { |
877 |
elsif ( defined $text_filename ) { |
| 853 |
print $fh @output_chunks; |
878 |
print $fh @output_chunks; |
| 854 |
} |
879 |
} |
| 855 |
elsif ($nomail){ |
880 |
elsif ($nomail){ |
| 856 |
local $, = "\f"; # pagebreak |
881 |
local $, = "\f"; # pagebreak |
| 857 |
- |
|
|