|
Lines 298-311
alert them of items that have just become due.
Link Here
|
| 298 |
|
298 |
|
| 299 |
# These variables are set by command line options. |
299 |
# These variables are set by command line options. |
| 300 |
# They are initially set to default values. |
300 |
# They are initially set to default values. |
| 301 |
my $dbh = C4::Context->dbh(); |
301 |
my $dbh = C4::Context->dbh(); |
| 302 |
my $help = 0; |
302 |
my $help = 0; |
| 303 |
my $man = 0; |
303 |
my $man = 0; |
| 304 |
my $verbose = 0; |
304 |
my $verbose = 0; |
| 305 |
my $nomail = 0; |
305 |
my $nomail = 0; |
| 306 |
my $MAX = 90; |
306 |
my $MAX = 90; |
| 307 |
my $test_mode = 0; |
307 |
my $test_mode = 0; |
| 308 |
my $frombranch = 'item-issuebranch'; |
308 |
my $frombranch = 'item-issuebranch'; |
|
|
309 |
my $itype_level = C4::Context->preference('item-level_itypes') ? 'item' : 'biblioitem'; |
| 309 |
my @branchcodes; # Branch(es) passed as parameter |
310 |
my @branchcodes; # Branch(es) passed as parameter |
| 310 |
my @emails_to_use; # Emails to use for messaging |
311 |
my @emails_to_use; # Emails to use for messaging |
| 311 |
my @emails; # Emails given in command-line parameters |
312 |
my @emails; # Emails given in command-line parameters |
|
Lines 443-448
if ( defined $csvfilename ) {
Link Here
|
| 443 |
} |
444 |
} |
| 444 |
|
445 |
|
| 445 |
@branches = @overduebranches unless @branches; |
446 |
@branches = @overduebranches unless @branches; |
|
|
447 |
|
| 448 |
# Setup output file if requested |
| 446 |
our $fh; |
449 |
our $fh; |
| 447 |
if ( defined $htmlfilename ) { |
450 |
if ( defined $htmlfilename ) { |
| 448 |
if ( $htmlfilename eq '' ) { |
451 |
if ( $htmlfilename eq '' ) { |
|
Lines 462-471
if ( defined $htmlfilename ) {
Link Here
|
| 462 |
} |
465 |
} |
| 463 |
} |
466 |
} |
| 464 |
|
467 |
|
|
|
468 |
# Setup category list |
| 469 |
my @categories; |
| 470 |
if (@myborcat) { |
| 471 |
@categories = @myborcat; |
| 472 |
} elsif (@myborcatout) { |
| 473 |
@categories = Koha::Patron::Categories->search( { catagorycode => { 'not_in' => \@myborcatout } } ) |
| 474 |
->get_column('categorycode'); |
| 475 |
} else { |
| 476 |
@categories = Koha::Patron::Categories->search()->get_column('categorycode'); |
| 477 |
} |
| 478 |
|
| 479 |
# Setup itemtype list |
| 480 |
my @itemtypes; |
| 481 |
if (@myitemtypes) { |
| 482 |
@itemtypes = @myitemtypes; |
| 483 |
} elsif (@myitemtypesout) { |
| 484 |
@itemtypes = |
| 485 |
Koha::ItemTypes->search( { itemtype => { 'not_in' => \@myitemtypesout } } )->get_column('itemtype'); |
| 486 |
} else { |
| 487 |
@itemtypes = Koha::ItemTypes->search()->get_column('itemtype'); |
| 488 |
} |
| 489 |
|
| 465 |
my %already_queued; |
490 |
my %already_queued; |
| 466 |
my %seen = map { $_ => 1 } @branches; |
491 |
my %seen = map { $_ => 1 } @branches; |
| 467 |
|
492 |
|
| 468 |
# Work through branches |
493 |
# Work through branches |
|
|
494 |
my @output_chunks; |
| 469 |
foreach my $branchcode (@branches) { |
495 |
foreach my $branchcode (@branches) { |
| 470 |
my $calendar; |
496 |
my $calendar; |
| 471 |
if ( C4::Context->preference('OverdueNoticeCalendar') ) { |
497 |
if ( C4::Context->preference('OverdueNoticeCalendar') ) { |
|
Lines 479-534
foreach my $branchcode (@branches) {
Link Here
|
| 479 |
my $admin_email_address = $library->from_email_address; |
505 |
my $admin_email_address = $library->from_email_address; |
| 480 |
my $branch_email_address = C4::Context->preference('AddressForFailedOverdueNotices') |
506 |
my $branch_email_address = C4::Context->preference('AddressForFailedOverdueNotices') |
| 481 |
|| $library->inbound_email_address; |
507 |
|| $library->inbound_email_address; |
| 482 |
my @output_chunks; # may be sent to mail or stdout or csv file. |
508 |
@output_chunks = (); # may be sent to mail or stdout or csv file. |
| 483 |
|
509 |
|
| 484 |
$verbose and print "======================================\n"; |
510 |
$verbose and print "======================================\n"; |
| 485 |
$verbose and warn sprintf "branchcode : '%s' using %s\n", $branchcode, $branch_email_address; |
511 |
$verbose and warn sprintf "branchcode : '%s' using %s\n", $branchcode, $branch_email_address; |
| 486 |
|
512 |
|
| 487 |
my $sql2 = <<"END_SQL"; |
513 |
# Work through patron categories |
| 488 |
SELECT biblio.*, items.*, issues.*, biblioitems.itemtype, branchname |
514 |
for my $category (@categories) { |
| 489 |
FROM issues,items,biblio, biblioitems, branches b |
515 |
|
| 490 |
WHERE items.itemnumber=issues.itemnumber |
516 |
# Fetch all overdues for patron categories that want overdues and where the item is not lost and group by borrower |
| 491 |
AND biblio.biblionumber = items.biblionumber |
517 |
my $borrower_sql = <<"END_SQL"; |
| 492 |
AND b.branchcode = items.homebranch |
518 |
SELECT |
| 493 |
AND biblio.biblionumber = biblioitems.biblionumber |
519 |
issues.borrowernumber, |
| 494 |
AND issues.borrowernumber = ? |
520 |
borrowers.firstname, |
| 495 |
AND items.itemlost = 0 |
521 |
borrowers.surname, |
|
|
522 |
borrowers.address, |
| 523 |
borrowers.address2, |
| 524 |
borrowers.city, |
| 525 |
borrowers.zipcode, |
| 526 |
borrowers.country, |
| 527 |
borrowers.email, |
| 528 |
borrowers.emailpro, |
| 529 |
borrowers.B_email, |
| 530 |
borrowers.smsalertnumber, |
| 531 |
borrowers.phone, |
| 532 |
borrowers.cardnumber, |
| 533 |
biblio.*, |
| 534 |
biblioitems.itemtype AS b_itemtype, |
| 535 |
items.*, |
| 536 |
issues.*, |
| 537 |
branches.branchname |
| 538 |
FROM |
| 539 |
issues |
| 540 |
JOIN |
| 541 |
borrowers ON issues.borrowernumber = borrowers.borrowernumber |
| 542 |
JOIN |
| 543 |
categories ON borrowers.categorycode = categories.categorycode |
| 544 |
JOIN |
| 545 |
items ON issues.itemnumber = items.itemnumber |
| 546 |
JOIN |
| 547 |
biblio ON biblio.biblionumber = items.biblionumber |
| 548 |
JOIN |
| 549 |
biblioitems ON biblio.biblionumber = biblioitems.biblionumber |
| 550 |
JOIN |
| 551 |
branches ON branches.branchcode = items.homebranch |
| 552 |
WHERE |
| 553 |
items.itemlost = 0 |
| 496 |
AND TO_DAYS($date)-TO_DAYS(issues.date_due) >= 0 |
554 |
AND TO_DAYS($date)-TO_DAYS(issues.date_due) >= 0 |
| 497 |
END_SQL |
555 |
END_SQL |
| 498 |
|
556 |
|
| 499 |
if ($owning_library) { |
557 |
my @borrower_parameters; |
| 500 |
$sql2 .= ' AND items.homebranch = ? ' unless ($patron_homelibrary); |
558 |
if ($branchcode) { |
| 501 |
} else { |
559 |
if ($owning_library) { |
| 502 |
$sql2 .= ' AND issues.branchcode = ? ' unless ($patron_homelibrary); |
560 |
$borrower_sql .= ' AND items.homebranch=? '; |
| 503 |
} |
561 |
} else { |
| 504 |
my $sth2 = $dbh->prepare($sql2); |
562 |
$borrower_sql .= ' AND issues.branchcode=? '; |
| 505 |
|
563 |
} |
| 506 |
my @categories; |
564 |
push @borrower_parameters, $branchcode; |
| 507 |
if (@myborcat) { |
565 |
} |
| 508 |
@categories = @myborcat; |
566 |
if ($category) { |
| 509 |
} elsif (@myborcatout) { |
567 |
$borrower_sql .= ' AND borrowers.categorycode=? '; |
| 510 |
@categories = Koha::Patron::Categories->search( { catagorycode => { 'not_in' => \@myborcatout } } ) |
568 |
push @borrower_parameters, $category; |
| 511 |
->get_column('categorycode'); |
569 |
} |
| 512 |
} else { |
570 |
if (@itemtypes) { |
| 513 |
@categories = Koha::Patron::Categories->search()->get_column('categorycode'); |
571 |
my $placeholders = join( ", ", ("?") x @itemtypes ); |
| 514 |
} |
572 |
if ( $itype_level eq 'item' ) { |
|
|
573 |
$borrower_sql .= " AND items.itype IN ($placeholders) "; |
| 574 |
} else { |
| 575 |
$borrower_sql .= " AND biblioitems.itemtype IN ($placeholders) "; |
| 576 |
} |
| 577 |
push @borrower_parameters, @itemtypes; |
| 578 |
} |
| 579 |
$borrower_sql .= ' AND categories.overduenoticerequired=1 ORDER BY issues.borrowernumber'; |
| 580 |
|
| 581 |
# $sth gets borrower info if at least one overdue item has triggered the overdue action. |
| 582 |
my $sth = $dbh->prepare($borrower_sql); |
| 583 |
$sth->execute(@borrower_parameters); |
| 584 |
|
| 585 |
if ( $verbose > 1 ) { |
| 586 |
warn sprintf "--------Borrower SQL------\n"; |
| 587 |
warn $borrower_sql |
| 588 |
. "\n $branchcode | " |
| 589 |
. "'$category' | " |
| 590 |
. join( "','", @itemtypes ) . " | " |
| 591 |
. $date_to_run->datetime() . ")\n"; |
| 592 |
warn sprintf "--------------------------\n"; |
| 593 |
} |
| 594 |
$verbose and warn sprintf "Found %s overdues for $category on $date_to_run\n", $sth->rows; |
| 515 |
|
595 |
|
| 516 |
my @itemtypes; |
596 |
my $borrowernumber; |
| 517 |
if (@myitemtypes) { |
597 |
my $borrower_overdues = {}; |
| 518 |
@itemtypes = @myitemtypes; |
|
|
| 519 |
} elsif (@myitemtypesout) { |
| 520 |
@itemtypes = |
| 521 |
Koha::ItemTypes->search( { itemtype => { 'not_in' => \@myitemtypesout } } )->get_column('itemtype'); |
| 522 |
} else { |
| 523 |
@itemtypes = Koha::ItemTypes->search()->get_column('itemtype'); |
| 524 |
} |
| 525 |
|
598 |
|
| 526 |
# Work through category and itemtype combinations to catch overdue rules for each |
599 |
# Iterate over all overdues |
| 527 |
for my $category (@categories) { |
600 |
OVERDUE: while ( my $data = $sth->fetchrow_hashref ) { |
| 528 |
for my $itemtype (@itemtypes) { |
601 |
my $itemtype = $data->{itype} // $data->{b_itemtype}; |
| 529 |
my $i = 0; |
602 |
|
|
|
603 |
# Collect triggers or act on them if we're switching borrower |
| 604 |
if ( !defined $borrower_overdues->{borrowernumber} ) { |
| 605 |
if ($verbose) { |
| 606 |
warn "\n-----------------------------------------\n"; |
| 607 |
warn "Collecting overdue triggers for borrower " . $data->{borrowernumber} . "\n"; |
| 608 |
} |
| 609 |
$borrower_overdues = { borrowernumber => $data->{borrowernumber}, branchcode => $branchcode }; |
| 610 |
} elsif ( $borrower_overdues->{borrowernumber} ne $data->{borrowernumber} ) { |
| 611 |
$verbose and warn "Collected overdue triggers for " . $borrower_overdues->{borrowernumber} . "\n"; |
| 612 |
_enact_trigger($borrower_overdues); |
| 613 |
if ($verbose) { |
| 614 |
warn "\n-----------------------------------------\n"; |
| 615 |
warn "Collecting overdue triggers for borrower " . $data->{borrowernumber} . "\n"; |
| 616 |
} |
| 617 |
$borrower_overdues = { borrowernumber => $data->{borrowernumber}, branchcode => $branchcode }; |
| 618 |
} |
| 530 |
|
619 |
|
| 531 |
# Work through triggers until we run out of rules |
620 |
$verbose |
|
|
621 |
and warn "\nProcessing overdue " |
| 622 |
. $data->{issue_id} |
| 623 |
. " with branch = '$branchcode', categorycode = '$category' and itemtype = '$itemtype'\n"; |
| 624 |
|
| 625 |
# Work through triggers until we run out of rules or find a match |
| 626 |
my $i = 0; |
| 532 |
PERIOD: while (1) { |
627 |
PERIOD: while (1) { |
| 533 |
$i++; |
628 |
$i++; |
| 534 |
my $ii = $i + 1; |
629 |
my $ii = $i + 1; |
|
Lines 543-549
END_SQL
Link Here
|
| 543 |
itemtype => $itemtype, |
638 |
itemtype => $itemtype, |
| 544 |
} |
639 |
} |
| 545 |
); |
640 |
); |
| 546 |
$verbose and warn "branch '$branchcode', categorycode = $category, itemtype = $itemtype, pass $i\n"; |
|
|
| 547 |
|
641 |
|
| 548 |
if ( !defined( $overdue_rules->{ "overdue_$i" . '_delay' } ) ) { |
642 |
if ( !defined( $overdue_rules->{ "overdue_$i" . '_delay' } ) ) { |
| 549 |
last PERIOD; |
643 |
last PERIOD; |
|
Lines 558-572
END_SQL
Link Here
|
| 558 |
: ($MAX) |
652 |
: ($MAX) |
| 559 |
); # issues being more than maxdays late are managed somewhere else. (borrower probably suspended) |
653 |
); # issues being more than maxdays late are managed somewhere else. (borrower probably suspended) |
| 560 |
|
654 |
|
| 561 |
next unless defined $mindays; |
|
|
| 562 |
|
| 563 |
if ( !$overdue_rules->{ "overdue_$i" . '_notice' } ) { |
655 |
if ( !$overdue_rules->{ "overdue_$i" . '_notice' } ) { |
| 564 |
$verbose and warn sprintf "No letter code found for pass %s\n", $i; |
656 |
$verbose and warn sprintf "Trigger %s skipped, No letter code found\n", $i; |
| 565 |
next PERIOD; |
657 |
next PERIOD; |
| 566 |
} |
658 |
} |
| 567 |
$verbose |
|
|
| 568 |
and warn sprintf "Using letter code '%s' for pass %s\n", |
| 569 |
$overdue_rules->{ "overdue_$i" . '_notice' }, $i; |
| 570 |
|
659 |
|
| 571 |
# $letter->{'content'} is the text of the mail that is sent. |
660 |
# $letter->{'content'} is the text of the mail that is sent. |
| 572 |
# this text contains fields that are replaced by their value. Those fields must be written between brackets |
661 |
# this text contains fields that are replaced by their value. Those fields must be written between brackets |
|
Lines 574-913
END_SQL
Link Here
|
| 574 |
# 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). |
663 |
# 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). |
| 575 |
# <date> <itemcount> <firstname> <lastname> <address1> <address2> <address3> <city> <postcode> <country> |
664 |
# <date> <itemcount> <firstname> <lastname> <address1> <address2> <address3> <city> <postcode> <country> |
| 576 |
|
665 |
|
| 577 |
# Fetch all overdue issues where the item is not lost and the borrower category wants overdue notices grouping by borrower |
666 |
# Check the overdue period matches |
| 578 |
my $borrower_sql = <<"END_SQL"; |
667 |
my $days_between; |
| 579 |
SELECT issues.borrowernumber, firstname, surname, address, address2, city, zipcode, country, email, emailpro, B_email, smsalertnumber, phone, cardnumber, date_due |
668 |
if ( C4::Context->preference('OverdueNoticeCalendar') ) { |
| 580 |
FROM issues,borrowers,categories,items |
669 |
$days_between = $calendar->days_between( |
| 581 |
WHERE issues.borrowernumber=borrowers.borrowernumber |
670 |
dt_from_string( $data->{date_due} ), |
| 582 |
AND borrowers.categorycode=categories.categorycode |
671 |
$date_to_run |
| 583 |
AND issues.itemnumber = items.itemnumber |
672 |
); |
| 584 |
AND items.itemlost = 0 |
673 |
} else { |
| 585 |
AND TO_DAYS($date)-TO_DAYS(issues.date_due) >= 0 |
674 |
$days_between = |
| 586 |
END_SQL |
675 |
$date_to_run->delta_days( dt_from_string( $data->{date_due} ) ); |
| 587 |
my @borrower_parameters; |
|
|
| 588 |
if ($branchcode) { |
| 589 |
if ($owning_library) { |
| 590 |
$borrower_sql .= ' AND items.homebranch=? '; |
| 591 |
} else { |
| 592 |
$borrower_sql .= ' AND issues.branchcode=? '; |
| 593 |
} |
| 594 |
push @borrower_parameters, $branchcode; |
| 595 |
} |
| 596 |
if ($category) { |
| 597 |
$borrower_sql .= ' AND borrowers.categorycode=? '; |
| 598 |
push @borrower_parameters, $category; |
| 599 |
} |
| 600 |
$borrower_sql .= ' AND categories.overduenoticerequired=1 ORDER BY issues.borrowernumber'; |
| 601 |
|
| 602 |
# $sth gets borrower info if at least one overdue item has triggered the overdue action. |
| 603 |
my $sth = $dbh->prepare($borrower_sql); |
| 604 |
$sth->execute(@borrower_parameters); |
| 605 |
|
| 606 |
if ( $verbose > 1 ) { |
| 607 |
warn sprintf "--------Borrower SQL------\n"; |
| 608 |
warn $borrower_sql |
| 609 |
. "\n $branchcode | " |
| 610 |
. $category |
| 611 |
. "\n ($mindays, $maxdays, " |
| 612 |
. $date_to_run->datetime() . ")\n"; |
| 613 |
warn sprintf "--------------------------\n"; |
| 614 |
} |
676 |
} |
| 615 |
$verbose and warn sprintf "Found %s borrowers with overdues\n", $sth->rows; |
677 |
$days_between = $days_between->in_units('days'); |
| 616 |
my $borrowernumber; |
678 |
if ($listall) { |
| 617 |
|
679 |
unless ( $days_between >= 1 and $days_between <= $MAX ) { |
| 618 |
# Iterate over all overdue issues |
680 |
next; |
| 619 |
while ( my $data = $sth->fetchrow_hashref ) { |
|
|
| 620 |
|
| 621 |
# check the borrower has at least one item that matches |
| 622 |
my $days_between; |
| 623 |
if ( C4::Context->preference('OverdueNoticeCalendar') ) { |
| 624 |
$days_between = $calendar->days_between( |
| 625 |
dt_from_string( $data->{date_due} ), |
| 626 |
$date_to_run |
| 627 |
); |
| 628 |
} else { |
| 629 |
$days_between = |
| 630 |
$date_to_run->delta_days( dt_from_string( $data->{date_due} ) ); |
| 631 |
} |
681 |
} |
| 632 |
$days_between = $days_between->in_units('days'); |
682 |
} else { |
| 633 |
if ($triggered) { |
683 |
if ($triggered) { |
| 634 |
if ( $mindays != $days_between ) { |
684 |
if ( $mindays != $days_between ) { |
|
|
685 |
$verbose and warn "Overdue skipped for trigger $i\n"; |
| 635 |
next; |
686 |
next; |
| 636 |
} |
687 |
} |
| 637 |
} else { |
688 |
} else { |
| 638 |
unless ( $days_between >= $mindays |
689 |
unless ( $days_between >= $mindays |
| 639 |
&& $days_between <= $maxdays ) |
690 |
&& $days_between <= $maxdays ) |
| 640 |
{ |
691 |
{ |
|
|
692 |
$verbose and warn "Overdue skipped for trigger $i\n"; |
| 641 |
next; |
693 |
next; |
| 642 |
} |
694 |
} |
| 643 |
} |
695 |
} |
| 644 |
if ( defined $borrowernumber && $borrowernumber eq $data->{'borrowernumber'} ) { |
696 |
} |
| 645 |
|
|
|
| 646 |
# we have already dealt with this borrower |
| 647 |
$verbose and warn "already dealt with this borrower $borrowernumber"; |
| 648 |
next; |
| 649 |
} |
| 650 |
$borrowernumber = $data->{'borrowernumber'}; |
| 651 |
|
| 652 |
# Skip duplicate borrowers as we'll have already handled them |
| 653 |
next if ( $patron_homelibrary && $already_queued{"$borrowernumber$i"} ); |
| 654 |
|
697 |
|
|
|
698 |
if ($verbose) { |
| 655 |
my $borr = sprintf( |
699 |
my $borr = sprintf( |
| 656 |
"%s%s%s (%s)", |
700 |
"%s%s%s (%s)", |
| 657 |
$data->{'surname'} || '', |
701 |
$data->{'surname'} || '', |
| 658 |
$data->{'firstname'} && $data->{'surname'} ? ', ' : '', |
702 |
$data->{'firstname'} && $data->{'surname'} ? ', ' : '', |
| 659 |
$data->{'firstname'} || '', |
703 |
$data->{'firstname'} || '', |
| 660 |
$borrowernumber |
704 |
$data->{borrowernumber} |
| 661 |
); |
705 |
); |
| 662 |
$verbose and warn "borrower $borr has items triggering level $i.\n"; |
706 |
warn sprintf "Overdue matched trigger %s with delay of %s days and overdue due date of %s\n", |
| 663 |
|
707 |
$i, |
| 664 |
my $patron = Koha::Patrons->find($borrowernumber); |
708 |
$triggered, $overdue_rules->{ "overdue_$i" . '_delay' }, $data->{date_due}; |
| 665 |
if ($patron_homelibrary) { |
709 |
warn sprintf "Using letter code '%s'\n", |
| 666 |
$branchcode = $patron->branchcode; |
710 |
$overdue_rules->{ "overdue_$i" . '_notice' }; |
| 667 |
$library = Koha::Libraries->find($branchcode); |
711 |
} |
| 668 |
$admin_email_address = $library->from_email_address; |
|
|
| 669 |
$branch_email_address = C4::Context->preference('AddressForFailedOverdueNotices') |
| 670 |
|| $library->inbound_email_address; |
| 671 |
} |
| 672 |
@emails_to_use = (); |
| 673 |
my $notice_email = $patron->notice_email_address; |
| 674 |
unless ($nomail) { |
| 675 |
if (@emails) { |
| 676 |
foreach (@emails) { |
| 677 |
push @emails_to_use, $data->{$_} if ( $data->{$_} ); |
| 678 |
} |
| 679 |
} else { |
| 680 |
push @emails_to_use, $notice_email if ($notice_email); |
| 681 |
} |
| 682 |
} |
| 683 |
|
| 684 |
my $letter = Koha::Notice::Templates->find_effective_template( |
| 685 |
{ |
| 686 |
module => 'circulation', |
| 687 |
code => $overdue_rules->{ "overdue_$i" . '_notice' }, |
| 688 |
branchcode => $branchcode, |
| 689 |
lang => $patron->lang |
| 690 |
} |
| 691 |
); |
| 692 |
|
| 693 |
unless ($letter) { |
| 694 |
$verbose and warn qq|Message '$overdue_rules->{"overdue_$i"."_notice"}' content not found|; |
| 695 |
|
| 696 |
# might as well skip while PERIOD, no other borrowers are going to work. |
| 697 |
# FIXME : Does this mean a letter must be defined in order to trigger a debar ? |
| 698 |
next PERIOD; |
| 699 |
} |
| 700 |
|
| 701 |
if ( $overdue_rules->{ "overdue_$i" . '_restrict' } ) { |
| 702 |
|
| 703 |
#action taken is debarring |
| 704 |
AddUniqueDebarment( |
| 705 |
{ |
| 706 |
borrowernumber => $borrowernumber, |
| 707 |
type => 'OVERDUES', |
| 708 |
comment => "OVERDUES_PROCESS " . output_pref( dt_from_string() ), |
| 709 |
} |
| 710 |
) unless $test_mode; |
| 711 |
$verbose and warn "debarring $borr\n"; |
| 712 |
} |
| 713 |
my @params = $patron_homelibrary ? ($borrowernumber) : ( $borrowernumber, $branchcode ); |
| 714 |
|
| 715 |
$sth2->execute(@params); |
| 716 |
my $itemcount = 0; |
| 717 |
my $titles = ""; |
| 718 |
my @items = (); |
| 719 |
|
| 720 |
my $j = 0; |
| 721 |
my $exceededPrintNoticesMaxLines = 0; |
| 722 |
|
| 723 |
# Get each overdue item for this patron |
| 724 |
while ( my $item_info = $sth2->fetchrow_hashref() ) { |
| 725 |
if ( C4::Context->preference('OverdueNoticeCalendar') ) { |
| 726 |
$days_between = |
| 727 |
$calendar->days_between( dt_from_string( $item_info->{date_due} ), $date_to_run ); |
| 728 |
} else { |
| 729 |
$days_between = $date_to_run->delta_days( dt_from_string( $item_info->{date_due} ) ); |
| 730 |
} |
| 731 |
$days_between = $days_between->in_units('days'); |
| 732 |
if ($listall) { |
| 733 |
unless ( $days_between >= 1 and $days_between <= $MAX ) { |
| 734 |
next; |
| 735 |
} |
| 736 |
} else { |
| 737 |
if ($triggered) { |
| 738 |
if ( $mindays != $days_between ) { |
| 739 |
next; |
| 740 |
} |
| 741 |
} else { |
| 742 |
unless ( $days_between >= $mindays |
| 743 |
&& $days_between <= $maxdays ) |
| 744 |
{ |
| 745 |
next; |
| 746 |
} |
| 747 |
} |
| 748 |
} |
| 749 |
|
| 750 |
if ( ( scalar(@emails_to_use) == 0 || $nomail ) |
| 751 |
&& $PrintNoticesMaxLines |
| 752 |
&& $j >= $PrintNoticesMaxLines ) |
| 753 |
{ |
| 754 |
$exceededPrintNoticesMaxLines = 1; |
| 755 |
last; |
| 756 |
} |
| 757 |
next if $patron_homelibrary and !grep { $seen{ $item_info->{branchcode} } } @branches; |
| 758 |
$j++; |
| 759 |
|
| 760 |
$titles .= C4::Letters::get_item_content( |
| 761 |
{ item => $item_info, item_content_fields => \@item_content_fields, dateonly => 1 } ); |
| 762 |
$itemcount++; |
| 763 |
push @items, $item_info; |
| 764 |
} |
| 765 |
$sth2->finish; |
| 766 |
|
| 767 |
my @message_transport_types = split( /,/, $overdue_rules->{ "overdue_$i" . '_mtt' } ); |
| 768 |
|
| 769 |
my $print_sent = 0; # A print notice is not yet sent for this patron |
| 770 |
for my $mtt (@message_transport_types) { |
| 771 |
next if $mtt eq 'itiva'; |
| 772 |
my $effective_mtt = $mtt; |
| 773 |
if ( ( $mtt eq 'email' and not scalar @emails_to_use ) |
| 774 |
or ( $mtt eq 'sms' and not $data->{smsalertnumber} ) ) |
| 775 |
{ |
| 776 |
# email or sms is requested but not exist, do a print. |
| 777 |
$effective_mtt = 'print'; |
| 778 |
} |
| 779 |
splice @items, $PrintNoticesMaxLines |
| 780 |
if $effective_mtt eq 'print' |
| 781 |
&& $PrintNoticesMaxLines |
| 782 |
&& scalar @items > $PrintNoticesMaxLines; |
| 783 |
|
| 784 |
#catch the case where we are sending a print to someone with an email |
| 785 |
|
| 786 |
my $letter_exists = Koha::Notice::Templates->find_effective_template( |
| 787 |
{ |
| 788 |
module => 'circulation', |
| 789 |
code => $overdue_rules->{ "overdue_$i" . '_notice' }, |
| 790 |
message_transport_type => $effective_mtt, |
| 791 |
branchcode => $branchcode, |
| 792 |
lang => $patron->lang |
| 793 |
} |
| 794 |
); |
| 795 |
|
| 796 |
my $letter = parse_overdues_letter( |
| 797 |
{ |
| 798 |
letter_code => $overdue_rules->{ "overdue_$i" . '_notice' }, |
| 799 |
borrowernumber => $borrowernumber, |
| 800 |
branchcode => $branchcode, |
| 801 |
items => \@items, |
| 802 |
substitute => |
| 803 |
{ # this appears to be a hack to overcome incomplete features in this code. |
| 804 |
bib => $library->branchname, # maybe 'bib' is a typo for 'lib<rary>'? |
| 805 |
'items.content' => $titles, |
| 806 |
'count' => $itemcount, |
| 807 |
}, |
| 808 |
|
| 809 |
# If there is no template defined for the requested letter |
| 810 |
# Fallback on the original type |
| 811 |
message_transport_type => $letter_exists ? $effective_mtt : $mtt, |
| 812 |
} |
| 813 |
); |
| 814 |
unless ( $letter && $letter->{content} ) { |
| 815 |
$verbose and warn qq|Message '$overdue_rules->{"overdue_$i"."_notice"}' content not found|; |
| 816 |
|
| 817 |
# this transport doesn't have a configured notice, so try another |
| 818 |
next; |
| 819 |
} |
| 820 |
|
| 821 |
if ($exceededPrintNoticesMaxLines) { |
| 822 |
$letter->{'content'} .= |
| 823 |
"List too long for form; please check your account online for a complete list of your overdue items."; |
| 824 |
} |
| 825 |
|
| 826 |
my @misses = grep { /./ } map { /^([^>]*)[>]+/; ( $1 || '' ); } split /\</, |
| 827 |
$letter->{'content'}; |
| 828 |
if (@misses) { |
| 829 |
$verbose |
| 830 |
and warn "The following terms were not matched and replaced: \n\t" . join "\n\t", |
| 831 |
@misses; |
| 832 |
} |
| 833 |
|
712 |
|
| 834 |
if ($nomail) { |
713 |
my @message_transport_types = split( /,/, $overdue_rules->{ "overdue_$i" . '_mtt' } ); |
| 835 |
push @output_chunks, |
714 |
for my $mtt (@message_transport_types) { |
| 836 |
prepare_letter_for_printing( |
715 |
push @{ $borrower_overdues->{triggers}->{$i}->{ $overdue_rules->{ "overdue_$i" . '_notice' } } |
| 837 |
{ |
716 |
->{$mtt} }, $data; |
| 838 |
letter => $letter, |
|
|
| 839 |
borrowernumber => $borrowernumber, |
| 840 |
firstname => $data->{'firstname'}, |
| 841 |
lastname => $data->{'surname'}, |
| 842 |
address1 => $data->{'address'}, |
| 843 |
address2 => $data->{'address2'}, |
| 844 |
city => $data->{'city'}, |
| 845 |
phone => $data->{'phone'}, |
| 846 |
cardnumber => $data->{'cardnumber'}, |
| 847 |
branchname => $library->branchname, |
| 848 |
letternumber => $i, |
| 849 |
postcode => $data->{'zipcode'}, |
| 850 |
country => $data->{'country'}, |
| 851 |
email => $notice_email, |
| 852 |
itemcount => $itemcount, |
| 853 |
titles => $titles, |
| 854 |
outputformat => defined $csvfilename ? 'csv' |
| 855 |
: defined $htmlfilename ? 'html' |
| 856 |
: defined $text_filename ? 'text' |
| 857 |
: '', |
| 858 |
} |
| 859 |
); |
| 860 |
} else { |
| 861 |
if ( ( $mtt eq 'email' and not scalar @emails_to_use ) |
| 862 |
or ( $mtt eq 'sms' and not $data->{smsalertnumber} ) ) |
| 863 |
{ |
| 864 |
push @output_chunks, |
| 865 |
prepare_letter_for_printing( |
| 866 |
{ |
| 867 |
letter => $letter, |
| 868 |
borrowernumber => $borrowernumber, |
| 869 |
firstname => $data->{'firstname'}, |
| 870 |
lastname => $data->{'surname'}, |
| 871 |
address1 => $data->{'address'}, |
| 872 |
address2 => $data->{'address2'}, |
| 873 |
city => $data->{'city'}, |
| 874 |
postcode => $data->{'zipcode'}, |
| 875 |
country => $data->{'country'}, |
| 876 |
email => $notice_email, |
| 877 |
itemcount => $itemcount, |
| 878 |
titles => $titles, |
| 879 |
outputformat => defined $csvfilename ? 'csv' |
| 880 |
: defined $htmlfilename ? 'html' |
| 881 |
: defined $text_filename ? 'text' |
| 882 |
: '', |
| 883 |
} |
| 884 |
); |
| 885 |
} |
| 886 |
unless ( $effective_mtt eq 'print' and $print_sent == 1 ) { |
| 887 |
|
| 888 |
# Just sent a print if not already done. |
| 889 |
C4::Letters::EnqueueLetter( |
| 890 |
{ |
| 891 |
letter => $letter, |
| 892 |
borrowernumber => $borrowernumber, |
| 893 |
message_transport_type => $effective_mtt, |
| 894 |
from_address => $admin_email_address, |
| 895 |
to_address => join( ',', @emails_to_use ), |
| 896 |
reply_address => $library->inbound_email_address, |
| 897 |
} |
| 898 |
) unless $test_mode; |
| 899 |
|
| 900 |
# A print notice should be sent only once per overdue level. |
| 901 |
# 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. |
| 902 |
$print_sent = 1 if $effective_mtt eq 'print'; |
| 903 |
} |
| 904 |
} |
| 905 |
} |
| 906 |
$already_queued{"$borrowernumber$i"} = 1; |
| 907 |
} |
717 |
} |
| 908 |
$sth->finish; |
718 |
if ( $overdue_rules->{ "overdue_$i" . '_restrict' } ) { |
|
|
719 |
$borrower_overdues->{restrict} = 1; |
| 720 |
} |
| 721 |
$borrower_overdues->{'email'} = $data->{'email'}; |
| 722 |
$borrower_overdues->{'emailpro'} = $data->{'emailpro'}; |
| 723 |
$borrower_overdues->{'B_email'} = $data->{'B_email'}; |
| 724 |
$borrower_overdues->{'smsalertnumber'} = $data->{'smsalertnumber'}; |
| 725 |
$borrower_overdues->{'phone'} = $data->{'phone'}; |
| 909 |
} |
726 |
} |
| 910 |
} |
727 |
} |
|
|
728 |
$sth->finish; |
| 729 |
|
| 730 |
# Catch final trigger |
| 731 |
if ( $borrower_overdues->{borrowernumber} ) { |
| 732 |
$verbose and warn "Collected overdue triggers for " . $borrower_overdues->{borrowernumber} . "\n"; |
| 733 |
_enact_trigger($borrower_overdues); |
| 734 |
$borrower_overdues = {}; |
| 735 |
} |
| 911 |
} |
736 |
} |
| 912 |
|
737 |
|
| 913 |
if (@output_chunks) { |
738 |
if (@output_chunks) { |
|
Lines 964-971
END_SQL
Link Here
|
| 964 |
) unless $test_mode; |
789 |
) unless $test_mode; |
| 965 |
} |
790 |
} |
| 966 |
} |
791 |
} |
| 967 |
|
|
|
| 968 |
} |
792 |
} |
|
|
793 |
|
| 969 |
if ($csvfilename) { |
794 |
if ($csvfilename) { |
| 970 |
|
795 |
|
| 971 |
# note that we're not testing on $csv_fh to prevent closing |
796 |
# note that we're not testing on $csv_fh to prevent closing |
|
Lines 984-989
if ( defined $htmlfilename ) {
Link Here
|
| 984 |
|
809 |
|
| 985 |
These methods are internal to the operation of overdue_notices.pl. |
810 |
These methods are internal to the operation of overdue_notices.pl. |
| 986 |
|
811 |
|
|
|
812 |
=cut |
| 813 |
|
| 814 |
sub _enact_trigger { |
| 815 |
my ($borrower_overdues) = @_; |
| 816 |
|
| 817 |
my $borrowernumber = $borrower_overdues->{borrowernumber}; |
| 818 |
my $branchcode = $borrower_overdues->{branchcode}; |
| 819 |
my $patron = Koha::Patrons->find($borrowernumber); |
| 820 |
my ( $library, $admin_email_address, $branch_email_address ); |
| 821 |
$library = Koha::Libraries->find($branchcode); |
| 822 |
|
| 823 |
if ($patron_homelibrary) { |
| 824 |
$branchcode = $patron->branchcode; |
| 825 |
$library = Koha::Libraries->find($branchcode); |
| 826 |
$admin_email_address = $library->from_email_address; |
| 827 |
$branch_email_address = C4::Context->preference('AddressForFailedOverdueNotices') |
| 828 |
|| $library->inbound_email_address; |
| 829 |
} |
| 830 |
@emails_to_use = (); |
| 831 |
my $notice_email = $patron->notice_email_address; |
| 832 |
unless ($nomail) { |
| 833 |
if (@emails) { |
| 834 |
foreach (@emails) { |
| 835 |
push @emails_to_use, $borrower_overdues->{$_} if ( $borrower_overdues->{$_} ); |
| 836 |
} |
| 837 |
} else { |
| 838 |
push @emails_to_use, $notice_email if ($notice_email); |
| 839 |
} |
| 840 |
} |
| 841 |
|
| 842 |
for my $trigger ( sort keys %{ $borrower_overdues->{triggers} } ) { |
| 843 |
for my $notice ( keys %{ $borrower_overdues->{triggers}->{$trigger} } ) { |
| 844 |
my $print_sent = 0; # A print notice is not yet sent for this patron |
| 845 |
for my $mtt ( keys %{ $borrower_overdues->{triggers}->{$trigger}->{$notice} } ) { |
| 846 |
|
| 847 |
next if $mtt eq 'itiva'; |
| 848 |
my $effective_mtt = $mtt; |
| 849 |
if ( ( $mtt eq 'email' and not scalar @emails_to_use ) |
| 850 |
or ( $mtt eq 'sms' and not $borrower_overdues->{smsalertnumber} ) ) |
| 851 |
{ |
| 852 |
# email or sms is requested but not exist, do a print. |
| 853 |
$effective_mtt = 'print'; |
| 854 |
} |
| 855 |
|
| 856 |
my $j = 0; |
| 857 |
my $exceededPrintNoticesMaxLines = 0; |
| 858 |
|
| 859 |
# Get each overdue item for this trigger |
| 860 |
my $itemcount = 0; |
| 861 |
my $titles = ""; |
| 862 |
my @items = (); |
| 863 |
for my $item_info ( @{ $borrower_overdues->{triggers}->{$trigger}->{$notice}->{$effective_mtt} } ) { |
| 864 |
if ( ( scalar(@emails_to_use) == 0 || $nomail ) |
| 865 |
&& $PrintNoticesMaxLines |
| 866 |
&& $j >= $PrintNoticesMaxLines ) |
| 867 |
{ |
| 868 |
$exceededPrintNoticesMaxLines = 1; |
| 869 |
last; |
| 870 |
} |
| 871 |
next if $patron_homelibrary and !grep { $seen{ $item_info->{branchcode} } } @branches; |
| 872 |
$j++; |
| 873 |
|
| 874 |
$titles .= C4::Letters::get_item_content( |
| 875 |
{ item => $item_info, item_content_fields => \@item_content_fields, dateonly => 1 } ); |
| 876 |
$itemcount++; |
| 877 |
push @items, $item_info; |
| 878 |
} |
| 879 |
|
| 880 |
splice @items, $PrintNoticesMaxLines |
| 881 |
if $effective_mtt eq 'print' |
| 882 |
&& $PrintNoticesMaxLines |
| 883 |
&& scalar @items > $PrintNoticesMaxLines; |
| 884 |
|
| 885 |
#catch the case where we are sending a print to someone with an email |
| 886 |
|
| 887 |
my $letter_exists = Koha::Notice::Templates->find_effective_template( |
| 888 |
{ |
| 889 |
module => 'circulation', |
| 890 |
code => $notice, |
| 891 |
message_transport_type => $effective_mtt, |
| 892 |
branchcode => $branchcode, |
| 893 |
lang => $patron->lang |
| 894 |
} |
| 895 |
); |
| 896 |
|
| 897 |
unless ($letter_exists) { |
| 898 |
$verbose and warn qq|Message '$notice' for '$effective_mtt' content not found|; |
| 899 |
next; |
| 900 |
} |
| 901 |
|
| 902 |
my $letter = parse_overdues_letter( |
| 903 |
{ |
| 904 |
letter_code => $notice, |
| 905 |
borrowernumber => $borrowernumber, |
| 906 |
branchcode => $branchcode, |
| 907 |
items => \@items, |
| 908 |
substitute => { # this appears to be a hack to overcome incomplete features in this code. |
| 909 |
bib => $library->branchname, # maybe 'bib' is a typo for 'lib<rary>'? |
| 910 |
'items.content' => $titles, |
| 911 |
'count' => $itemcount, |
| 912 |
}, |
| 913 |
|
| 914 |
# If there is no template defined for the requested letter |
| 915 |
# Fallback on the original type |
| 916 |
message_transport_type => $letter_exists ? $effective_mtt : $mtt, |
| 917 |
} |
| 918 |
); |
| 919 |
unless ( $letter && $letter->{content} ) { |
| 920 |
$verbose and warn qq|Message '$notice' content not found|; |
| 921 |
|
| 922 |
# this transport doesn't have a configured notice, so try another |
| 923 |
next; |
| 924 |
} |
| 925 |
|
| 926 |
if ($exceededPrintNoticesMaxLines) { |
| 927 |
$letter->{'content'} .= |
| 928 |
"List too long for form; please check your account online for a complete list of your overdue items."; |
| 929 |
} |
| 930 |
|
| 931 |
my @misses = grep { /./ } map { /^([^>]*)[>]+/; ( $1 || '' ); } split /\</, |
| 932 |
$letter->{'content'}; |
| 933 |
if (@misses) { |
| 934 |
$verbose |
| 935 |
and warn "The following terms were not matched and replaced: \n\t" . join "\n\t", |
| 936 |
@misses; |
| 937 |
} |
| 938 |
|
| 939 |
if ($nomail) { |
| 940 |
push @output_chunks, |
| 941 |
prepare_letter_for_printing( |
| 942 |
{ |
| 943 |
letter => $letter, |
| 944 |
borrowernumber => $borrowernumber, |
| 945 |
firstname => $borrower_overdues->{'firstname'}, |
| 946 |
lastname => $borrower_overdues->{'surname'}, |
| 947 |
address1 => $borrower_overdues->{'address'}, |
| 948 |
address2 => $borrower_overdues->{'address2'}, |
| 949 |
city => $borrower_overdues->{'city'}, |
| 950 |
phone => $borrower_overdues->{'phone'}, |
| 951 |
cardnumber => $borrower_overdues->{'cardnumber'}, |
| 952 |
branchname => $library->branchname, |
| 953 |
letternumber => $trigger, |
| 954 |
postcode => $borrower_overdues->{'zipcode'}, |
| 955 |
country => $borrower_overdues->{'country'}, |
| 956 |
email => $notice_email, |
| 957 |
itemcount => $itemcount, |
| 958 |
titles => $titles, |
| 959 |
outputformat => defined $csvfilename ? 'csv' |
| 960 |
: defined $htmlfilename ? 'html' |
| 961 |
: defined $text_filename ? 'text' |
| 962 |
: '', |
| 963 |
} |
| 964 |
); |
| 965 |
} else { |
| 966 |
if ( ( $mtt eq 'email' and not scalar @emails_to_use ) |
| 967 |
or ( $mtt eq 'sms' and not $borrower_overdues->{smsalertnumber} ) ) |
| 968 |
{ |
| 969 |
push @output_chunks, |
| 970 |
prepare_letter_for_printing( |
| 971 |
{ |
| 972 |
letter => $letter, |
| 973 |
borrowernumber => $borrowernumber, |
| 974 |
firstname => $borrower_overdues->{'firstname'}, |
| 975 |
lastname => $borrower_overdues->{'surname'}, |
| 976 |
address1 => $borrower_overdues->{'address'}, |
| 977 |
address2 => $borrower_overdues->{'address2'}, |
| 978 |
city => $borrower_overdues->{'city'}, |
| 979 |
postcode => $borrower_overdues->{'zipcode'}, |
| 980 |
country => $borrower_overdues->{'country'}, |
| 981 |
email => $notice_email, |
| 982 |
itemcount => $itemcount, |
| 983 |
titles => $titles, |
| 984 |
outputformat => defined $csvfilename ? 'csv' |
| 985 |
: defined $htmlfilename ? 'html' |
| 986 |
: defined $text_filename ? 'text' |
| 987 |
: '', |
| 988 |
} |
| 989 |
); |
| 990 |
} |
| 991 |
unless ( $effective_mtt eq 'print' and $print_sent == 1 ) { |
| 992 |
|
| 993 |
# Just sent a print if not already done. |
| 994 |
C4::Letters::EnqueueLetter( |
| 995 |
{ |
| 996 |
letter => $letter, |
| 997 |
borrowernumber => $borrowernumber, |
| 998 |
message_transport_type => $effective_mtt, |
| 999 |
from_address => $admin_email_address, |
| 1000 |
to_address => join( ',', @emails_to_use ), |
| 1001 |
reply_address => $library->inbound_email_address, |
| 1002 |
} |
| 1003 |
) unless $test_mode; |
| 1004 |
|
| 1005 |
# A print notice should be sent only once per overdue level. |
| 1006 |
# 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. |
| 1007 |
$print_sent = 1 if $effective_mtt eq 'print'; |
| 1008 |
} |
| 1009 |
} |
| 1010 |
} |
| 1011 |
|
| 1012 |
$already_queued{"$borrowernumber$trigger"} = 1; |
| 1013 |
} |
| 1014 |
} |
| 1015 |
|
| 1016 |
if ( $borrower_overdues->{restrict} ) { |
| 1017 |
|
| 1018 |
#action taken is debarring |
| 1019 |
AddUniqueDebarment( |
| 1020 |
{ |
| 1021 |
borrowernumber => $borrowernumber, |
| 1022 |
type => 'OVERDUES', |
| 1023 |
comment => "OVERDUES_PROCESS " . output_pref( dt_from_string() ), |
| 1024 |
} |
| 1025 |
) unless $test_mode; |
| 1026 |
|
| 1027 |
my $borr = sprintf( |
| 1028 |
"%s%s%s (%s)", |
| 1029 |
$borrower_overdues->{'surname'} || '', |
| 1030 |
$borrower_overdues->{'firstname'} && $borrower_overdues->{'surname'} ? ', ' : '', |
| 1031 |
$borrower_overdues->{'firstname'} || '', |
| 1032 |
$borrower_overdues->{borrowernumber} |
| 1033 |
); |
| 1034 |
$verbose and warn "debarring $borr\n"; |
| 1035 |
} |
| 1036 |
} |
| 1037 |
|
| 987 |
=head2 prepare_letter_for_printing |
1038 |
=head2 prepare_letter_for_printing |
| 988 |
|
1039 |
|
| 989 |
returns a string of text appropriate for printing in the event that an |
1040 |
returns a string of text appropriate for printing in the event that an |
| 990 |
- |
|
|