Lines 29-38
use DateTime::Duration;
Link Here
|
29 |
use Koha::Script -cron; |
29 |
use Koha::Script -cron; |
30 |
use C4::Context; |
30 |
use C4::Context; |
31 |
use C4::Letters; |
31 |
use C4::Letters; |
32 |
use C4::Overdues qw( parse_overdues_letter ); |
32 |
use C4::Overdues qw( parse_overdues_letter ); |
33 |
use C4::Log qw( cronlogaction ); |
33 |
use C4::Log qw( cronlogaction ); |
34 |
use Koha::Patron::Debarments qw( AddUniqueDebarment ); |
34 |
use Koha::Patron::Debarments qw( AddUniqueDebarment ); |
35 |
use Koha::DateUtils qw( dt_from_string output_pref ); |
35 |
use Koha::DateUtils qw( dt_from_string output_pref ); |
36 |
use Koha::Calendar; |
36 |
use Koha::Calendar; |
37 |
use Koha::Libraries; |
37 |
use Koha::Libraries; |
38 |
use Koha::Acquisition::Currencies; |
38 |
use Koha::Acquisition::Currencies; |
Lines 298-319
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 @branchcodes; # Branch(es) passed as parameter |
309 |
my $itype_level = C4::Context->preference('item-level_itypes') ? 'item' : 'biblioitem'; |
|
|
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 |
312 |
my $csvfilename; |
313 |
my $csvfilename; |
313 |
my $htmlfilename; |
314 |
my $htmlfilename; |
314 |
my $text_filename; |
315 |
my $text_filename; |
315 |
my $triggered = 0; |
316 |
my $triggered = 0; |
316 |
my $listall = 0; |
317 |
my $listall = 0; |
317 |
my $itemscontent = join( ',', qw( date_due title barcode author itemnumber ) ); |
318 |
my $itemscontent = join( ',', qw( date_due title barcode author itemnumber ) ); |
318 |
my @myitemtypes; |
319 |
my @myitemtypes; |
319 |
my @myitemtypesout; |
320 |
my @myitemtypesout; |
Lines 321-327
my @myborcat;
Link Here
|
321 |
my @myborcatout; |
322 |
my @myborcatout; |
322 |
my ( $date_input, $today ); |
323 |
my ( $date_input, $today ); |
323 |
|
324 |
|
324 |
my $command_line_options = join(" ",@ARGV); |
325 |
my $command_line_options = join( " ", @ARGV ); |
325 |
|
326 |
|
326 |
GetOptions( |
327 |
GetOptions( |
327 |
'help|?' => \$help, |
328 |
'help|?' => \$help, |
Lines 359-378
die "--frombranch takes item-homebranch or item-issuebranch or patron-homebranch
Link Here
|
359 |
|| $frombranch eq 'patron-homebranch' ); |
360 |
|| $frombranch eq 'patron-homebranch' ); |
360 |
$frombranch = |
361 |
$frombranch = |
361 |
C4::Context->preference('OverdueNoticeFrom') ne 'cron' ? C4::Context->preference('OverdueNoticeFrom') : $frombranch; |
362 |
C4::Context->preference('OverdueNoticeFrom') ne 'cron' ? C4::Context->preference('OverdueNoticeFrom') : $frombranch; |
362 |
my $owning_library = ( $frombranch eq 'item-homebranch' ) ? 1 : 0; |
363 |
my $owning_library = ( $frombranch eq 'item-homebranch' ) ? 1 : 0; |
363 |
my $patron_homelibrary = ( $frombranch eq 'patron-homebranch' ) ? 1 : 0; |
364 |
my $patron_homelibrary = ( $frombranch eq 'patron-homebranch' ) ? 1 : 0; |
364 |
|
365 |
|
365 |
my @overduebranches = C4::Overdues::GetBranchcodesWithOverdueRules(); # Branches with overdue rules |
366 |
my @overduebranches = C4::Overdues::GetBranchcodesWithOverdueRules(); # Branches with overdue rules |
366 |
my @branches; # Branches passed as parameter with overdue rules |
367 |
my @branches; # Branches passed as parameter with overdue rules |
367 |
my $branchcount = scalar(@overduebranches); |
368 |
my $branchcount = scalar(@overduebranches); |
368 |
|
369 |
|
369 |
my $overduebranch_word = scalar @overduebranches > 1 ? 'branches' : 'branch'; |
370 |
my $overduebranch_word = scalar @overduebranches > 1 ? 'branches' : 'branch'; |
370 |
my $branchcodes_word = scalar @branchcodes > 1 ? 'branches' : 'branch'; |
371 |
my $branchcodes_word = scalar @branchcodes > 1 ? 'branches' : 'branch'; |
371 |
|
372 |
|
372 |
my $PrintNoticesMaxLines = C4::Context->preference('PrintNoticesMaxLines'); |
373 |
my $PrintNoticesMaxLines = C4::Context->preference('PrintNoticesMaxLines'); |
373 |
|
374 |
|
374 |
if ($branchcount) { |
375 |
if ($branchcount) { |
375 |
$verbose and warn "Found $branchcount $overduebranch_word with first message enabled: " . join( ', ', map { "'$_'" } @overduebranches ), "\n"; |
376 |
$verbose |
|
|
377 |
and warn "Found $branchcount $overduebranch_word with first message enabled: " |
378 |
. join( ', ', map { "'$_'" } @overduebranches ), "\n"; |
376 |
} else { |
379 |
} else { |
377 |
die 'No branches with active overduerules'; |
380 |
die 'No branches with active overduerules'; |
378 |
} |
381 |
} |
Lines 384-419
if (@branchcodes) {
Link Here
|
384 |
my %seen = map { $_ => 1 } @branchcodes; |
387 |
my %seen = map { $_ => 1 } @branchcodes; |
385 |
@branches = grep { $seen{$_} } @overduebranches; |
388 |
@branches = grep { $seen{$_} } @overduebranches; |
386 |
|
389 |
|
387 |
|
|
|
388 |
if (@branches) { |
390 |
if (@branches) { |
389 |
|
391 |
|
390 |
my $branch_word = scalar @branches > 1 ? 'branches' : 'branch'; |
392 |
my $branch_word = scalar @branches > 1 ? 'branches' : 'branch'; |
391 |
$verbose and warn "$branch_word @branches have overdue rules\n"; |
393 |
$verbose and warn "$branch_word @branches have overdue rules\n"; |
392 |
|
394 |
|
393 |
} else { |
395 |
} else { |
394 |
|
396 |
|
395 |
$verbose and warn "No active overduerules for $branchcodes_word '@branchcodes'\n"; |
397 |
$verbose and warn "No active overduerules for $branchcodes_word '@branchcodes'\n"; |
396 |
( scalar grep { '' eq $_ } @branches ) |
398 |
( scalar grep { '' eq $_ } @branches ) |
397 |
or die "No active overduerules for DEFAULT either!"; |
399 |
or die "No active overduerules for DEFAULT either!"; |
398 |
$verbose and warn "Falling back on default rules for @branchcodes\n"; |
400 |
$verbose and warn "Falling back on default rules for @branchcodes\n"; |
399 |
@branches = (''); |
401 |
@branches = (''); |
400 |
} |
402 |
} |
401 |
} |
403 |
} |
402 |
my $date_to_run; |
404 |
my $date_to_run; |
403 |
my $date; |
405 |
my $date; |
404 |
if ( $date_input ){ |
406 |
if ($date_input) { |
405 |
eval { |
407 |
eval { $date_to_run = dt_from_string( $date_input, 'iso' ); }; |
406 |
$date_to_run = dt_from_string( $date_input, 'iso' ); |
|
|
407 |
}; |
408 |
die "$date_input is not a valid date, aborting! Use a date in format YYYY-MM-DD." |
408 |
die "$date_input is not a valid date, aborting! Use a date in format YYYY-MM-DD." |
409 |
if $@ or not $date_to_run; |
409 |
if $@ or not $date_to_run; |
410 |
|
410 |
|
411 |
# It's certainly useless to escape $date_input |
411 |
# It's certainly useless to escape $date_input |
412 |
# dt_from_string should not return something if $date_input is not correctly set. |
412 |
# dt_from_string should not return something if $date_input is not correctly set. |
413 |
$date = $dbh->quote( $date_input ); |
413 |
$date = $dbh->quote($date_input); |
414 |
} |
414 |
} else { |
415 |
else { |
415 |
$date = "NOW()"; |
416 |
$date="NOW()"; |
|
|
417 |
$date_to_run = dt_from_string(); |
416 |
$date_to_run = dt_from_string(); |
418 |
} |
417 |
} |
419 |
|
418 |
|
Lines 422-439
my @item_content_fields = split( /,/, $itemscontent );
Link Here
|
422 |
|
421 |
|
423 |
binmode( STDOUT, ':encoding(UTF-8)' ); |
422 |
binmode( STDOUT, ':encoding(UTF-8)' ); |
424 |
|
423 |
|
425 |
|
|
|
426 |
our $csv; # the Text::CSV_XS object |
424 |
our $csv; # the Text::CSV_XS object |
427 |
our $csv_fh; # the filehandle to the CSV file. |
425 |
our $csv_fh; # the filehandle to the CSV file. |
428 |
if ( defined $csvfilename ) { |
426 |
if ( defined $csvfilename ) { |
429 |
my $sep_char = C4::Context->csv_delimiter; |
427 |
my $sep_char = C4::Context->csv_delimiter; |
430 |
$csv = Text::CSV_XS->new( { binary => 1 , sep_char => $sep_char } ); |
428 |
$csv = Text::CSV_XS->new( { binary => 1, sep_char => $sep_char } ); |
431 |
if ( $csvfilename eq '' ) { |
429 |
if ( $csvfilename eq '' ) { |
432 |
$csv_fh = *STDOUT; |
430 |
$csv_fh = *STDOUT; |
433 |
} else { |
431 |
} else { |
434 |
open $csv_fh, ">", $csvfilename or die "unable to open $csvfilename: $!"; |
432 |
open $csv_fh, ">", $csvfilename or die "unable to open $csvfilename: $!"; |
435 |
} |
433 |
} |
436 |
if ( $csv->combine(qw(name surname address1 address2 zipcode city country email phone cardnumber itemcount itemsinfo branchname letternumber)) ) { |
434 |
if ( |
|
|
435 |
$csv->combine( |
436 |
qw(name surname address1 address2 zipcode city country email phone cardnumber itemcount itemsinfo branchname letternumber) |
437 |
) |
438 |
) |
439 |
{ |
437 |
print $csv_fh $csv->string, "\n"; |
440 |
print $csv_fh $csv->string, "\n"; |
438 |
} else { |
441 |
} else { |
439 |
$verbose and warn 'combine failed on argument: ' . $csv->error_input; |
442 |
$verbose and warn 'combine failed on argument: ' . $csv->error_input; |
Lines 441-470
if ( defined $csvfilename ) {
Link Here
|
441 |
} |
444 |
} |
442 |
|
445 |
|
443 |
@branches = @overduebranches unless @branches; |
446 |
@branches = @overduebranches unless @branches; |
|
|
447 |
|
448 |
# Setup output file if requested |
444 |
our $fh; |
449 |
our $fh; |
445 |
if ( defined $htmlfilename ) { |
450 |
if ( defined $htmlfilename ) { |
446 |
if ( $htmlfilename eq '' ) { |
451 |
if ( $htmlfilename eq '' ) { |
447 |
$fh = *STDOUT; |
452 |
$fh = *STDOUT; |
448 |
} else { |
453 |
} else { |
449 |
my $today = dt_from_string(); |
454 |
my $today = dt_from_string(); |
450 |
open $fh, ">:encoding(UTF-8)",File::Spec->catdir ($htmlfilename,"notices-".$today->ymd().".html"); |
455 |
open $fh, ">:encoding(UTF-8)", File::Spec->catdir( $htmlfilename, "notices-" . $today->ymd() . ".html" ); |
451 |
} |
456 |
} |
452 |
|
457 |
|
453 |
print $fh _get_html_start(); |
458 |
print $fh _get_html_start(); |
|
|
459 |
} elsif ( defined $text_filename ) { |
460 |
if ( $text_filename eq '' ) { |
461 |
$fh = *STDOUT; |
462 |
} else { |
463 |
my $today = dt_from_string(); |
464 |
open $fh, ">:encoding(UTF-8)", File::Spec->catdir( $text_filename, "notices-" . $today->ymd() . ".txt" ); |
465 |
} |
466 |
} |
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'); |
454 |
} |
477 |
} |
455 |
elsif ( defined $text_filename ) { |
478 |
|
456 |
if ( $text_filename eq '' ) { |
479 |
# Setup itemtype list |
457 |
$fh = *STDOUT; |
480 |
my @itemtypes; |
458 |
} else { |
481 |
if (@myitemtypes) { |
459 |
my $today = dt_from_string(); |
482 |
@itemtypes = @myitemtypes; |
460 |
open $fh, ">:encoding(UTF-8)",File::Spec->catdir ($text_filename,"notices-".$today->ymd().".txt"); |
483 |
} elsif (@myitemtypesout) { |
461 |
} |
484 |
@itemtypes = |
|
|
485 |
Koha::ItemTypes->search( { itemtype => { 'not_in' => \@myitemtypesout } } )->get_column('itemtype'); |
486 |
} else { |
487 |
@itemtypes = Koha::ItemTypes->search()->get_column('itemtype'); |
462 |
} |
488 |
} |
463 |
|
489 |
|
464 |
my %already_queued; |
490 |
my %already_queued; |
465 |
my %seen = map { $_ => 1 } @branches; |
491 |
my %seen = map { $_ => 1 } @branches; |
466 |
|
492 |
|
467 |
# Work through branches |
493 |
# Work through branches |
|
|
494 |
my @output_chunks; |
468 |
foreach my $branchcode (@branches) { |
495 |
foreach my $branchcode (@branches) { |
469 |
my $calendar; |
496 |
my $calendar; |
470 |
if ( C4::Context->preference('OverdueNoticeCalendar') ) { |
497 |
if ( C4::Context->preference('OverdueNoticeCalendar') ) { |
Lines 474-535
foreach my $branchcode (@branches) {
Link Here
|
474 |
} |
501 |
} |
475 |
} |
502 |
} |
476 |
|
503 |
|
477 |
my $library = Koha::Libraries->find($branchcode); |
504 |
my $library = Koha::Libraries->find($branchcode); |
478 |
my $admin_email_address = $library->from_email_address; |
505 |
my $admin_email_address = $library->from_email_address; |
479 |
my $branch_email_address = C4::Context->preference('AddressForFailedOverdueNotices') |
506 |
my $branch_email_address = C4::Context->preference('AddressForFailedOverdueNotices') |
480 |
|| $library->inbound_email_address; |
507 |
|| $library->inbound_email_address; |
481 |
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. |
482 |
|
509 |
|
483 |
$verbose and print "======================================\n"; |
510 |
$verbose and print "======================================\n"; |
484 |
$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; |
485 |
|
512 |
|
486 |
my $sql2 = <<"END_SQL"; |
513 |
# Work through patron categories |
487 |
SELECT biblio.*, items.*, issues.*, biblioitems.itemtype, branchname |
514 |
for my $category (@categories) { |
488 |
FROM issues,items,biblio, biblioitems, branches b |
515 |
|
489 |
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 |
490 |
AND biblio.biblionumber = items.biblionumber |
517 |
my $borrower_sql = <<"END_SQL"; |
491 |
AND b.branchcode = items.homebranch |
518 |
SELECT |
492 |
AND biblio.biblionumber = biblioitems.biblionumber |
519 |
issues.borrowernumber, |
493 |
AND issues.borrowernumber = ? |
520 |
borrowers.firstname, |
494 |
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 |
495 |
AND TO_DAYS($date)-TO_DAYS(issues.date_due) >= 0 |
554 |
AND TO_DAYS($date)-TO_DAYS(issues.date_due) >= 0 |
496 |
END_SQL |
555 |
END_SQL |
497 |
|
556 |
|
498 |
if ($owning_library) { |
557 |
my @borrower_parameters; |
499 |
$sql2 .= ' AND items.homebranch = ? ' unless ($patron_homelibrary); |
558 |
if ($branchcode) { |
500 |
} else { |
559 |
if ($owning_library) { |
501 |
$sql2 .= ' AND issues.branchcode = ? ' unless ($patron_homelibrary); |
560 |
$borrower_sql .= ' AND items.homebranch=? '; |
502 |
} |
561 |
} else { |
503 |
my $sth2 = $dbh->prepare($sql2); |
562 |
$borrower_sql .= ' AND issues.branchcode=? '; |
504 |
|
563 |
} |
505 |
my @categories; |
564 |
push @borrower_parameters, $branchcode; |
506 |
if (@myborcat) { |
565 |
} |
507 |
@categories = @myborcat; |
566 |
if ($category) { |
508 |
} elsif (@myborcatout) { |
567 |
$borrower_sql .= ' AND borrowers.categorycode=? '; |
509 |
@categories = Koha::Patron::Categories->search( { catagorycode => { 'not_in' => \@myborcatout } } ) |
568 |
push @borrower_parameters, $category; |
510 |
->get_column('categorycode'); |
569 |
} |
511 |
} else { |
570 |
if (@itemtypes) { |
512 |
@categories = Koha::Patron::Categories->search()->get_column('categorycode'); |
571 |
my $placeholders = join( ", ", ("?") x @itemtypes ); |
513 |
} |
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; |
514 |
|
595 |
|
515 |
my @itemtypes; |
596 |
my $borrowernumber; |
516 |
if (@myitemtypes) { |
597 |
my $borrower_overdues = {}; |
517 |
@itemtypes = @myitemtypes; |
|
|
518 |
} elsif (@myitemtypesout) { |
519 |
@itemtypes = |
520 |
Koha::ItemTypes->search( { itemtype => { 'not_in' => \@myitemtypesout } } )->get_column('itemtype'); |
521 |
} else { |
522 |
@itemtypes = Koha::ItemTypes->search()->get_column('itemtype'); |
523 |
} |
524 |
|
598 |
|
525 |
# Work through category and itemtype combinations to catch overdue rules for each |
599 |
# Iterate over all overdues |
526 |
for my $category (@categories) { |
600 |
OVERDUE: while ( my $data = $sth->fetchrow_hashref ) { |
527 |
for my $itemtype (@itemtypes) { |
601 |
my $itemtype = $data->{itype} // $data->{b_itemtype}; |
|
|
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 |
} |
619 |
|
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 |
528 |
my $i = 0; |
626 |
my $i = 0; |
529 |
# Work through triggers until we run out of rules |
627 |
PERIOD: while (1) { |
530 |
PERIOD: while (1) { |
|
|
531 |
$i++; |
628 |
$i++; |
532 |
my $ii = $i + 1; |
629 |
my $ii = $i + 1; |
533 |
my $overdue_rules = Koha::CirculationRules->get_effective_rules( |
630 |
my $overdue_rules = Koha::CirculationRules->get_effective_rules( |
534 |
{ |
631 |
{ |
535 |
rules => [ |
632 |
rules => [ |
Lines 541-901
END_SQL
Link Here
|
541 |
itemtype => $itemtype, |
638 |
itemtype => $itemtype, |
542 |
} |
639 |
} |
543 |
); |
640 |
); |
544 |
$verbose and warn "branch '$branchcode', categorycode = $category, itemtype = $itemtype, pass $i\n"; |
|
|
545 |
|
641 |
|
546 |
if ( !defined( $overdue_rules->{ "overdue_$i" . '_delay' } ) ) { |
642 |
if ( !defined( $overdue_rules->{ "overdue_$i" . '_delay' } ) ) { |
547 |
last PERIOD; |
643 |
last PERIOD; |
548 |
} |
644 |
} |
549 |
|
645 |
|
550 |
my $mindays = |
646 |
my $mindays = |
551 |
$overdue_rules->{ "overdue_$i" . '_delay' }; # the notice will be sent after mindays days (grace period) |
647 |
$overdue_rules->{ "overdue_$i" . '_delay' } |
|
|
648 |
; # the notice will be sent after mindays days (grace period) |
552 |
my $maxdays = ( |
649 |
my $maxdays = ( |
553 |
$overdue_rules->{ "overdue_$ii" . '_delay' } |
650 |
$overdue_rules->{ "overdue_$ii" . '_delay' } |
554 |
? $overdue_rules->{ "overdue_$ii" . '_delay' } - 1 |
651 |
? $overdue_rules->{ "overdue_$ii" . '_delay' } - 1 |
555 |
: ($MAX) |
652 |
: ($MAX) |
556 |
); # 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) |
557 |
|
|
|
558 |
next unless defined $mindays; |
559 |
|
654 |
|
560 |
if ( !$overdue_rules->{"overdue_$i".'_notice'} ) { |
655 |
if ( !$overdue_rules->{ "overdue_$i" . '_notice' } ) { |
561 |
$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; |
562 |
next PERIOD; |
657 |
next PERIOD; |
563 |
} |
658 |
} |
564 |
$verbose and warn sprintf "Using letter code '%s' for pass %s\n", $overdue_rules->{"overdue_$i".'_notice'}, $i; |
|
|
565 |
|
659 |
|
566 |
# $letter->{'content'} is the text of the mail that is sent. |
660 |
# $letter->{'content'} is the text of the mail that is sent. |
567 |
# 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 |
568 |
# The following fields are available : |
662 |
# The following fields are available : |
569 |
# 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). |
570 |
# <date> <itemcount> <firstname> <lastname> <address1> <address2> <address3> <city> <postcode> <country> |
664 |
# <date> <itemcount> <firstname> <lastname> <address1> <address2> <address3> <city> <postcode> <country> |
571 |
|
665 |
|
572 |
# 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 |
573 |
my $borrower_sql = <<"END_SQL"; |
667 |
my $days_between; |
574 |
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') ) { |
575 |
FROM issues,borrowers,categories,items |
669 |
$days_between = $calendar->days_between( |
576 |
WHERE issues.borrowernumber=borrowers.borrowernumber |
670 |
dt_from_string( $data->{date_due} ), |
577 |
AND borrowers.categorycode=categories.categorycode |
671 |
$date_to_run |
578 |
AND issues.itemnumber = items.itemnumber |
672 |
); |
579 |
AND items.itemlost = 0 |
673 |
} else { |
580 |
AND TO_DAYS($date)-TO_DAYS(issues.date_due) >= 0 |
674 |
$days_between = |
581 |
END_SQL |
675 |
$date_to_run->delta_days( dt_from_string( $data->{date_due} ) ); |
582 |
my @borrower_parameters; |
|
|
583 |
if ($branchcode) { |
584 |
if($owning_library) { |
585 |
$borrower_sql .= ' AND items.homebranch=? '; |
586 |
} else { |
587 |
$borrower_sql .= ' AND issues.branchcode=? '; |
588 |
} |
589 |
push @borrower_parameters, $branchcode; |
590 |
} |
591 |
if ( $category ) { |
592 |
$borrower_sql .= ' AND borrowers.categorycode=? '; |
593 |
push @borrower_parameters, $category; |
594 |
} |
595 |
$borrower_sql .= ' AND categories.overduenoticerequired=1 ORDER BY issues.borrowernumber'; |
596 |
|
597 |
# $sth gets borrower info if at least one overdue item has triggered the overdue action. |
598 |
my $sth = $dbh->prepare($borrower_sql); |
599 |
$sth->execute(@borrower_parameters); |
600 |
|
601 |
if ( $verbose > 1 ){ |
602 |
warn sprintf "--------Borrower SQL------\n"; |
603 |
warn $borrower_sql . "\n $branchcode | " . $category . "\n ($mindays, $maxdays, ". $date_to_run->datetime() .")\n"; |
604 |
warn sprintf "--------------------------\n"; |
605 |
} |
676 |
} |
606 |
$verbose and warn sprintf "Found %s borrowers with overdues\n", $sth->rows; |
677 |
$days_between = $days_between->in_units('days'); |
607 |
my $borrowernumber; |
678 |
if ($listall) { |
608 |
# Iterate over all overdue issues |
679 |
unless ( $days_between >= 1 and $days_between <= $MAX ) { |
609 |
while ( my $data = $sth->fetchrow_hashref ) { |
680 |
next; |
610 |
|
|
|
611 |
# check the borrower has at least one item that matches |
612 |
my $days_between; |
613 |
if ( C4::Context->preference('OverdueNoticeCalendar') ) |
614 |
{ |
615 |
$days_between = |
616 |
$calendar->days_between( dt_from_string($data->{date_due}), |
617 |
$date_to_run ); |
618 |
} |
619 |
else { |
620 |
$days_between = |
621 |
$date_to_run->delta_days( dt_from_string($data->{date_due}) ); |
622 |
} |
681 |
} |
623 |
$days_between = $days_between->in_units('days'); |
682 |
} else { |
624 |
if ($triggered) { |
683 |
if ($triggered) { |
625 |
if ( $mindays != $days_between ) { |
684 |
if ( $mindays != $days_between ) { |
|
|
685 |
$verbose and warn "Overdue skipped for trigger $i\n"; |
626 |
next; |
686 |
next; |
627 |
} |
687 |
} |
628 |
} |
688 |
} else { |
629 |
else { |
689 |
unless ( $days_between >= $mindays |
630 |
unless ( $days_between >= $mindays |
|
|
631 |
&& $days_between <= $maxdays ) |
690 |
&& $days_between <= $maxdays ) |
632 |
{ |
691 |
{ |
|
|
692 |
$verbose and warn "Overdue skipped for trigger $i\n"; |
633 |
next; |
693 |
next; |
634 |
} |
694 |
} |
635 |
} |
695 |
} |
636 |
if (defined $borrowernumber && $borrowernumber eq $data->{'borrowernumber'}){ |
696 |
} |
637 |
# we have already dealt with this borrower |
|
|
638 |
$verbose and warn "already dealt with this borrower $borrowernumber"; |
639 |
next; |
640 |
} |
641 |
$borrowernumber = $data->{'borrowernumber'}; |
642 |
|
643 |
# Skip duplicate borrowers as we'll have already handled them |
644 |
next if ( $patron_homelibrary && $already_queued{"$borrowernumber$i"} ); |
645 |
|
697 |
|
646 |
my $borr = sprintf( "%s%s%s (%s)", |
698 |
if ($verbose) { |
|
|
699 |
my $borr = sprintf( |
700 |
"%s%s%s (%s)", |
647 |
$data->{'surname'} || '', |
701 |
$data->{'surname'} || '', |
648 |
$data->{'firstname'} && $data->{'surname'} ? ', ' : '', |
702 |
$data->{'firstname'} && $data->{'surname'} ? ', ' : '', |
649 |
$data->{'firstname'} || '', |
703 |
$data->{'firstname'} || '', |
650 |
$borrowernumber ); |
704 |
$data->{borrowernumber} |
651 |
$verbose and warn "borrower $borr has items triggering level $i.\n"; |
|
|
652 |
|
653 |
my $patron = Koha::Patrons->find( $borrowernumber ); |
654 |
if ($patron_homelibrary) { |
655 |
$branchcode = $patron->branchcode; |
656 |
$library = Koha::Libraries->find($branchcode); |
657 |
$admin_email_address = $library->from_email_address; |
658 |
$branch_email_address = C4::Context->preference('AddressForFailedOverdueNotices') |
659 |
|| $library->inbound_email_address; |
660 |
} |
661 |
@emails_to_use = (); |
662 |
my $notice_email = $patron->notice_email_address; |
663 |
unless ($nomail) { |
664 |
if (@emails) { |
665 |
foreach (@emails) { |
666 |
push @emails_to_use, $data->{$_} if ( $data->{$_} ); |
667 |
} |
668 |
} |
669 |
else { |
670 |
push @emails_to_use, $notice_email if ($notice_email); |
671 |
} |
672 |
} |
673 |
|
674 |
my $letter = Koha::Notice::Templates->find_effective_template( |
675 |
{ |
676 |
module => 'circulation', |
677 |
code => $overdue_rules->{"overdue_$i".'_notice'}, |
678 |
branchcode => $branchcode, |
679 |
lang => $patron->lang |
680 |
} |
681 |
); |
705 |
); |
|
|
706 |
warn sprintf "Overdue matched trigger %s with delay of %s days and overdue due date of %s\n", |
707 |
$i, |
708 |
$triggered, $overdue_rules->{ "overdue_$i" . '_delay' }, $data->{date_due}; |
709 |
warn sprintf "Using letter code '%s'\n", |
710 |
$overdue_rules->{ "overdue_$i" . '_notice' }; |
711 |
} |
682 |
|
712 |
|
683 |
unless ($letter) { |
713 |
my @message_transport_types = split( /,/, $overdue_rules->{ "overdue_$i" . '_mtt' } ); |
684 |
$verbose and warn qq|Message '$overdue_rules->{"overdue_$i"."_notice"}' content not found|; |
714 |
for my $mtt (@message_transport_types) { |
685 |
|
715 |
push @{ $borrower_overdues->{triggers}->{$i}->{ $overdue_rules->{ "overdue_$i" . '_notice' } } |
686 |
# might as well skip while PERIOD, no other borrowers are going to work. |
716 |
->{$mtt} }, $data; |
687 |
# FIXME : Does this mean a letter must be defined in order to trigger a debar ? |
|
|
688 |
next PERIOD; |
689 |
} |
690 |
|
691 |
if ( $overdue_rules->{"overdue_$i".'_restrict'} ) { |
692 |
|
693 |
#action taken is debarring |
694 |
AddUniqueDebarment( |
695 |
{ |
696 |
borrowernumber => $borrowernumber, |
697 |
type => 'OVERDUES', |
698 |
comment => "OVERDUES_PROCESS " . output_pref( dt_from_string() ), |
699 |
} |
700 |
) unless $test_mode; |
701 |
$verbose and warn "debarring $borr\n"; |
702 |
} |
703 |
my @params = $patron_homelibrary ? ($borrowernumber) : ( $borrowernumber, $branchcode ); |
704 |
|
705 |
$sth2->execute(@params); |
706 |
my $itemcount = 0; |
707 |
my $titles = ""; |
708 |
my @items = (); |
709 |
|
710 |
my $j = 0; |
711 |
my $exceededPrintNoticesMaxLines = 0; |
712 |
# Get each overdue item for this patron |
713 |
while ( my $item_info = $sth2->fetchrow_hashref() ) { |
714 |
if ( C4::Context->preference('OverdueNoticeCalendar') ) { |
715 |
$days_between = |
716 |
$calendar->days_between( |
717 |
dt_from_string( $item_info->{date_due} ), $date_to_run ); |
718 |
} |
719 |
else { |
720 |
$days_between = |
721 |
$date_to_run->delta_days( |
722 |
dt_from_string( $item_info->{date_due} ) ); |
723 |
} |
724 |
$days_between = $days_between->in_units('days'); |
725 |
if ($listall){ |
726 |
unless ($days_between >= 1 and $days_between <= $MAX){ |
727 |
next; |
728 |
} |
729 |
} |
730 |
else { |
731 |
if ($triggered) { |
732 |
if ( $mindays != $days_between ) { |
733 |
next; |
734 |
} |
735 |
} |
736 |
else { |
737 |
unless ( $days_between >= $mindays |
738 |
&& $days_between <= $maxdays ) |
739 |
{ |
740 |
next; |
741 |
} |
742 |
} |
743 |
} |
744 |
|
745 |
if ( ( scalar(@emails_to_use) == 0 || $nomail ) && $PrintNoticesMaxLines && $j >= $PrintNoticesMaxLines ) { |
746 |
$exceededPrintNoticesMaxLines = 1; |
747 |
last; |
748 |
} |
749 |
next if $patron_homelibrary and !grep { $seen{ $item_info->{branchcode} } } @branches; |
750 |
$j++; |
751 |
|
752 |
$titles .= C4::Letters::get_item_content( { item => $item_info, item_content_fields => \@item_content_fields, dateonly => 1 } ); |
753 |
$itemcount++; |
754 |
push @items, $item_info; |
755 |
} |
756 |
$sth2->finish; |
757 |
|
758 |
my @message_transport_types = split( /,/, $overdue_rules->{ "overdue_$i" . '_mtt' } ); |
759 |
|
760 |
my $print_sent = 0; # A print notice is not yet sent for this patron |
761 |
for my $mtt ( @message_transport_types ) { |
762 |
next if $mtt eq 'itiva'; |
763 |
my $effective_mtt = $mtt; |
764 |
if ( ($mtt eq 'email' and not scalar @emails_to_use) or ($mtt eq 'sms' and not $data->{smsalertnumber}) ) { |
765 |
# email or sms is requested but not exist, do a print. |
766 |
$effective_mtt = 'print'; |
767 |
} |
768 |
splice @items, $PrintNoticesMaxLines if $effective_mtt eq 'print' && $PrintNoticesMaxLines && scalar @items > $PrintNoticesMaxLines; |
769 |
#catch the case where we are sending a print to someone with an email |
770 |
|
771 |
my $letter_exists = Koha::Notice::Templates->find_effective_template( |
772 |
{ |
773 |
module => 'circulation', |
774 |
code => $overdue_rules->{ "overdue_$i" . '_notice' }, |
775 |
message_transport_type => $effective_mtt, |
776 |
branchcode => $branchcode, |
777 |
lang => $patron->lang |
778 |
} |
779 |
); |
780 |
|
781 |
my $letter = parse_overdues_letter( |
782 |
{ |
783 |
letter_code => $overdue_rules->{ "overdue_$i" . '_notice' }, |
784 |
borrowernumber => $borrowernumber, |
785 |
branchcode => $branchcode, |
786 |
items => \@items, |
787 |
substitute => { # this appears to be a hack to overcome incomplete features in this code. |
788 |
bib => $library->branchname, # maybe 'bib' is a typo for 'lib<rary>'? |
789 |
'items.content' => $titles, |
790 |
'count' => $itemcount, |
791 |
}, |
792 |
|
793 |
# If there is no template defined for the requested letter |
794 |
# Fallback on the original type |
795 |
message_transport_type => $letter_exists ? $effective_mtt : $mtt, |
796 |
} |
797 |
); |
798 |
unless ( $letter && $letter->{content} ) { |
799 |
$verbose and warn qq|Message '$overdue_rules->{"overdue_$i"."_notice"}' content not found|; |
800 |
|
801 |
# this transport doesn't have a configured notice, so try another |
802 |
next; |
803 |
} |
804 |
|
805 |
if ( $exceededPrintNoticesMaxLines ) { |
806 |
$letter->{'content'} .= "List too long for form; please check your account online for a complete list of your overdue items."; |
807 |
} |
808 |
|
809 |
my @misses = grep { /./ } map { /^([^>]*)[>]+/; ( $1 || '' ); } split /\</, $letter->{'content'}; |
810 |
if (@misses) { |
811 |
$verbose and warn "The following terms were not matched and replaced: \n\t" . join "\n\t", @misses; |
812 |
} |
813 |
|
814 |
if ($nomail) { |
815 |
push @output_chunks, |
816 |
prepare_letter_for_printing( |
817 |
{ letter => $letter, |
818 |
borrowernumber => $borrowernumber, |
819 |
firstname => $data->{'firstname'}, |
820 |
lastname => $data->{'surname'}, |
821 |
address1 => $data->{'address'}, |
822 |
address2 => $data->{'address2'}, |
823 |
city => $data->{'city'}, |
824 |
phone => $data->{'phone'}, |
825 |
cardnumber => $data->{'cardnumber'}, |
826 |
branchname => $library->branchname, |
827 |
letternumber => $i, |
828 |
postcode => $data->{'zipcode'}, |
829 |
country => $data->{'country'}, |
830 |
email => $notice_email, |
831 |
itemcount => $itemcount, |
832 |
titles => $titles, |
833 |
outputformat => defined $csvfilename ? 'csv' : defined $htmlfilename ? 'html' : defined $text_filename ? 'text' : '', |
834 |
} |
835 |
); |
836 |
} else { |
837 |
if ( ($mtt eq 'email' and not scalar @emails_to_use) or ($mtt eq 'sms' and not $data->{smsalertnumber}) ) { |
838 |
push @output_chunks, |
839 |
prepare_letter_for_printing( |
840 |
{ letter => $letter, |
841 |
borrowernumber => $borrowernumber, |
842 |
firstname => $data->{'firstname'}, |
843 |
lastname => $data->{'surname'}, |
844 |
address1 => $data->{'address'}, |
845 |
address2 => $data->{'address2'}, |
846 |
city => $data->{'city'}, |
847 |
postcode => $data->{'zipcode'}, |
848 |
country => $data->{'country'}, |
849 |
email => $notice_email, |
850 |
itemcount => $itemcount, |
851 |
titles => $titles, |
852 |
outputformat => defined $csvfilename ? 'csv' : defined $htmlfilename ? 'html' : defined $text_filename ? 'text' : '', |
853 |
} |
854 |
); |
855 |
} |
856 |
unless ( $effective_mtt eq 'print' and $print_sent == 1 ) { |
857 |
# Just sent a print if not already done. |
858 |
C4::Letters::EnqueueLetter( |
859 |
{ letter => $letter, |
860 |
borrowernumber => $borrowernumber, |
861 |
message_transport_type => $effective_mtt, |
862 |
from_address => $admin_email_address, |
863 |
to_address => join(',', @emails_to_use), |
864 |
reply_address => $library->inbound_email_address, |
865 |
} |
866 |
) unless $test_mode; |
867 |
# A print notice should be sent only once per overdue level. |
868 |
# 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. |
869 |
$print_sent = 1 if $effective_mtt eq 'print'; |
870 |
} |
871 |
} |
872 |
} |
873 |
$already_queued{"$borrowernumber$i"} = 1; |
874 |
} |
717 |
} |
875 |
$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'}; |
876 |
} |
726 |
} |
877 |
} |
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 |
} |
878 |
} |
736 |
} |
879 |
|
737 |
|
880 |
if (@output_chunks) { |
738 |
if (@output_chunks) { |
881 |
if ( defined $csvfilename ) { |
739 |
if ( defined $csvfilename ) { |
882 |
print $csv_fh @output_chunks; |
740 |
print $csv_fh @output_chunks; |
883 |
} |
741 |
} elsif ( defined $htmlfilename ) { |
884 |
elsif ( defined $htmlfilename ) { |
|
|
885 |
print $fh @output_chunks; |
742 |
print $fh @output_chunks; |
886 |
} |
743 |
} elsif ( defined $text_filename ) { |
887 |
elsif ( defined $text_filename ) { |
|
|
888 |
print $fh @output_chunks; |
744 |
print $fh @output_chunks; |
|
|
745 |
} elsif ($nomail) { |
746 |
local $, = "\f"; # pagebreak |
747 |
print @output_chunks; |
889 |
} |
748 |
} |
890 |
elsif ($nomail){ |
749 |
|
891 |
local $, = "\f"; # pagebreak |
|
|
892 |
print @output_chunks; |
893 |
} |
894 |
# Generate the content of the csv with headers |
750 |
# Generate the content of the csv with headers |
895 |
my $content; |
751 |
my $content; |
896 |
if ( defined $csvfilename ) { |
752 |
if ( defined $csvfilename ) { |
897 |
my $delimiter = C4::Context->csv_delimiter; |
753 |
my $delimiter = C4::Context->csv_delimiter; |
898 |
$content = join($delimiter, qw(title name surname address1 address2 zipcode city country email itemcount itemsinfo due_date issue_date)) . "\n"; |
754 |
$content = join( |
|
|
755 |
$delimiter, |
756 |
qw(title name surname address1 address2 zipcode city country email itemcount itemsinfo due_date issue_date) |
757 |
) . "\n"; |
899 |
$content .= join( "\n", @output_chunks ); |
758 |
$content .= join( "\n", @output_chunks ); |
900 |
} elsif ( defined $htmlfilename ) { |
759 |
} elsif ( defined $htmlfilename ) { |
901 |
$content = _get_html_start(); |
760 |
$content = _get_html_start(); |
Lines 907-914
END_SQL
Link Here
|
907 |
|
766 |
|
908 |
if ( C4::Context->preference('EmailOverduesNoEmail') ) { |
767 |
if ( C4::Context->preference('EmailOverduesNoEmail') ) { |
909 |
my $attachment = { |
768 |
my $attachment = { |
910 |
filename => defined $csvfilename ? 'attachment.csv' : defined $htmlfilename ? 'attachment.html' : 'attachment.txt', |
769 |
filename => defined $csvfilename ? 'attachment.csv' |
911 |
type => defined $htmlfilename ? 'text/html' : 'text/plain', |
770 |
: defined $htmlfilename ? 'attachment.html' |
|
|
771 |
: 'attachment.txt', |
772 |
type => defined $htmlfilename ? 'text/html' : 'text/plain', |
912 |
content => $content, |
773 |
content => $content, |
913 |
}; |
774 |
}; |
914 |
|
775 |
|
Lines 918-924
END_SQL
Link Here
|
918 |
}; |
779 |
}; |
919 |
|
780 |
|
920 |
C4::Letters::EnqueueLetter( |
781 |
C4::Letters::EnqueueLetter( |
921 |
{ letter => $letter, |
782 |
{ |
|
|
783 |
letter => $letter, |
922 |
borrowernumber => undef, |
784 |
borrowernumber => undef, |
923 |
message_transport_type => 'email', |
785 |
message_transport_type => 'email', |
924 |
attachments => [$attachment], |
786 |
attachments => [$attachment], |
Lines 927-951
END_SQL
Link Here
|
927 |
) unless $test_mode; |
789 |
) unless $test_mode; |
928 |
} |
790 |
} |
929 |
} |
791 |
} |
930 |
|
|
|
931 |
} |
792 |
} |
|
|
793 |
|
932 |
if ($csvfilename) { |
794 |
if ($csvfilename) { |
|
|
795 |
|
933 |
# 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 |
934 |
# STDOUT. |
797 |
# STDOUT. |
935 |
close $csv_fh; |
798 |
close $csv_fh; |
936 |
} |
799 |
} |
937 |
|
800 |
|
938 |
if ( defined $htmlfilename ) { |
801 |
if ( defined $htmlfilename ) { |
939 |
print $fh _get_html_end(); |
802 |
print $fh _get_html_end(); |
940 |
close $fh; |
803 |
close $fh; |
941 |
} elsif ( defined $text_filename ) { |
804 |
} elsif ( defined $text_filename ) { |
942 |
close $fh; |
805 |
close $fh; |
943 |
} |
806 |
} |
944 |
|
807 |
|
945 |
=head1 INTERNAL METHODS |
808 |
=head1 INTERNAL METHODS |
946 |
|
809 |
|
947 |
These methods are internal to the operation of overdue_notices.pl. |
810 |
These methods are internal to the operation of overdue_notices.pl. |
948 |
|
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 |
|
949 |
=head2 prepare_letter_for_printing |
1038 |
=head2 prepare_letter_for_printing |
950 |
|
1039 |
|
951 |
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 |
Lines 974-993
sub prepare_letter_for_printing {
Link Here
|
974 |
my $return; |
1063 |
my $return; |
975 |
chomp $params->{titles}; |
1064 |
chomp $params->{titles}; |
976 |
if ( exists $params->{'outputformat'} && $params->{'outputformat'} eq 'csv' ) { |
1065 |
if ( exists $params->{'outputformat'} && $params->{'outputformat'} eq 'csv' ) { |
977 |
if ($csv->combine( |
1066 |
if ( |
978 |
$params->{'firstname'}, $params->{'lastname'}, $params->{'address1'}, $params->{'address2'}, $params->{'postcode'}, |
1067 |
$csv->combine( |
979 |
$params->{'city'}, $params->{'country'}, $params->{'email'}, $params->{'phone'}, $params->{'cardnumber'}, |
1068 |
$params->{'firstname'}, $params->{'lastname'}, $params->{'address1'}, $params->{'address2'}, |
|
|
1069 |
$params->{'postcode'}, |
1070 |
$params->{'city'}, $params->{'country'}, $params->{'email'}, $params->{'phone'}, |
1071 |
$params->{'cardnumber'}, |
980 |
$params->{'itemcount'}, $params->{'titles'}, $params->{'branchname'}, $params->{'letternumber'} |
1072 |
$params->{'itemcount'}, $params->{'titles'}, $params->{'branchname'}, $params->{'letternumber'} |
981 |
) |
1073 |
) |
982 |
) { |
1074 |
) |
|
|
1075 |
{ |
983 |
return $csv->string, "\n"; |
1076 |
return $csv->string, "\n"; |
984 |
} else { |
1077 |
} else { |
985 |
$verbose and warn 'combine failed on argument: ' . $csv->error_input; |
1078 |
$verbose and warn 'combine failed on argument: ' . $csv->error_input; |
986 |
} |
1079 |
} |
987 |
} elsif ( exists $params->{'outputformat'} && $params->{'outputformat'} eq 'html' ) { |
1080 |
} elsif ( exists $params->{'outputformat'} && $params->{'outputformat'} eq 'html' ) { |
988 |
$return = "<pre>\n"; |
1081 |
$return = "<pre>\n"; |
989 |
$return .= "$params->{'letter'}->{'content'}\n"; |
1082 |
$return .= "$params->{'letter'}->{'content'}\n"; |
990 |
$return .= "\n</pre>\n"; |
1083 |
$return .= "\n</pre>\n"; |
991 |
} else { |
1084 |
} else { |
992 |
$return .= "$params->{'letter'}->{'content'}\n"; |
1085 |
$return .= "$params->{'letter'}->{'content'}\n"; |
993 |
|
1086 |
|
Lines 1034-1037
sub _get_html_end {
Link Here
|
1034 |
|
1127 |
|
1035 |
} |
1128 |
} |
1036 |
|
1129 |
|
1037 |
cronlogaction({ action => 'End', info => "COMPLETED" }); |
1130 |
cronlogaction( { action => 'End', info => "COMPLETED" } ); |
1038 |
- |
|
|