View | Details | Raw Unified | Return to bug 10190
Collapse All | Expand All

(-)a/misc/cronjobs/overdue_notices.pl (-413 / +505 lines)
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
- 

Return to bug 10190