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

(-)a/misc/cronjobs/overdue_notices.pl (-405 / +289 lines)
Lines 317-387 pod2usage( -verbose => 2 ) if $man; Link Here
317
317
318
cronlogaction();
318
cronlogaction();
319
319
320
if ( defined $csvfilename && $csvfilename =~ /^-/ ) {
320
sub VERBOSE{
321
    warn qq(using "$csvfilename" as filename, that seems odd);
321
    if ($verbose){
322
        printf STDERR "%s\n", shift;
323
    }
322
}
324
}
323
325
324
my @overduebranches    = C4::Overdues::GetBranchcodesWithOverdueRules();	# Branches with overdue rules
326
if ( defined $csvfilename && $csvfilename =~ /^-/ ) {
325
my @branches;									# Branches passed as parameter with overdue rules
327
    warn qq(using "$csvfilename" as filename, that seems odd);
326
my $branchcount = scalar(@overduebranches);
327
328
my $overduebranch_word = scalar @overduebranches > 1 ? 'branches' : 'branch';
329
my $branchcodes_word = scalar @branchcodes > 1 ? 'branches' : 'branch';
330
331
my $PrintNoticesMaxLines = C4::Context->preference('PrintNoticesMaxLines');
332
333
if ($branchcount) {
334
    $verbose and warn "Found $branchcount $overduebranch_word with first message enabled: " . join( ', ', map { "'$_'" } @overduebranches ), "\n";
335
} else {
336
    die 'No branches with active overduerules';
337
}
328
}
338
329
339
if (@branchcodes) {
340
    $verbose and warn "$branchcodes_word @branchcodes passed on parameter\n";
341
    
342
    # Getting libraries which have overdue rules
343
    my %seen = map { $_ => 1 } @branchcodes;
344
    @branches = grep { $seen{$_} } @overduebranches;
345
    
346
    
347
    if (@branches) {
348
349
    	my $branch_word = scalar @branches > 1 ? 'branches' : 'branch';
350
	$verbose and warn "$branch_word @branches have overdue rules\n";
351
352
    } else {
353
    
354
        $verbose and warn "No active overduerules for $branchcodes_word  '@branchcodes'\n";
355
        ( scalar grep { '' eq $_ } @branches )
356
          or die "No active overduerules for DEFAULT either!";
357
        $verbose and warn "Falling back on default rules for @branchcodes\n";
358
        @branches = ('');
359
    }
360
}
361
my $date_to_run;
330
my $date_to_run;
362
my $date;
331
my $date;
363
if ( $date_input ){
332
if ( $date_input ){
333
    $date = $dbh->quote($date);
364
    eval {
334
    eval {
365
        $date_to_run = dt_from_string( $date_input, 'iso' );
335
        $date_to_run = dt_from_string( $date_input );
366
    };
336
    };
367
    die "$date_input is not a valid date, aborting! Use a date in format YYYY-MM-DD."
337
    die "$date_input is not a valid date, aborting!"
368
        if $@ or not $date_to_run;
338
        if $@ or not $date_to_run;
369
339
370
    # It's certainly useless to escape $date_input
371
    # dt_from_string should not return something if $date_input is not correctly set.
372
    $date = $dbh->quote( $date_input );
373
}
340
}
374
else {
341
else {
375
    $date="NOW()";
342
    $date="NOW()";
376
    $date_to_run = dt_from_string();
343
    $date_to_run = dt_from_string();
377
}
344
}
378
345
346
##
347
## Some file/html preparation ?
348
##
349
350
my $printNoticesMaxLines = C4::Context->preference('PrintNoticesMaxLines');
351
379
# these are the fields that will be substituted into <<item.content>>
352
# these are the fields that will be substituted into <<item.content>>
380
my @item_content_fields = split( /,/, $itemscontent );
353
my @item_content_fields = split( /,/, $itemscontent );
381
354
382
binmode( STDOUT, ':encoding(UTF-8)' );
355
binmode( STDOUT, ':encoding(UTF-8)' );
383
356
384
385
our $csv;       # the Text::CSV_XS object
357
our $csv;       # the Text::CSV_XS object
386
our $csv_fh;    # the filehandle to the CSV file.
358
our $csv_fh;    # the filehandle to the CSV file.
387
if ( defined $csvfilename ) {
359
if ( defined $csvfilename ) {
Lines 393-418 if ( defined $csvfilename ) { Link Here
393
    } else {
365
    } else {
394
        open $csv_fh, ">", $csvfilename or die "unable to open $csvfilename: $!";
366
        open $csv_fh, ">", $csvfilename or die "unable to open $csvfilename: $!";
395
    }
367
    }
396
    if ( $csv->combine(qw(name surname address1 address2 zipcode city country email phone cardnumber itemcount itemsinfo branchname letternumber)) ) {
368
    if ( $csv->combine(qw(name surname address1 address2 zipcode city country email itemcount itemsinfo)) ) {
397
        print $csv_fh $csv->string, "\n";
369
        print $csv_fh $csv->string, "\n";
398
    } else {
370
    } else {
399
        $verbose and warn 'combine failed on argument: ' . $csv->error_input;
371
        VERBOSE( 'combine failed on argument: ' . $csv->error_input);
400
    }
372
    }
401
}
373
}
402
374
403
@branches = @overduebranches unless @branches;
404
our $fh;
375
our $fh;
405
if ( defined $htmlfilename ) {
376
if ( defined $htmlfilename ) {
406
  if ( $htmlfilename eq '' ) {
377
  if ( $htmlfilename eq '' ) {
407
    $fh = *STDOUT;
378
    $fh = *STDOUT;
408
  } else {
379
  } else {
409
    my $today = DateTime->now(time_zone => C4::Context->tz );
380
    my $today = DateTime->now(time_zone => C4::Context->tz );
410
    open $fh, ">:encoding(UTF-8)",File::Spec->catdir ($htmlfilename,"notices-".$today->ymd().".html");
381
    open $fh, ">",File::Spec->catdir ($htmlfilename,"notices-".$today->ymd().".html");
411
  }
382
  }
412
  
383
  
413
  print $fh "<html>\n";
384
  print $fh "<html>\n";
414
  print $fh "<head>\n";
385
  print $fh "<head>\n";
415
  print $fh "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
416
  print $fh "<style type='text/css'>\n";
386
  print $fh "<style type='text/css'>\n";
417
  print $fh "pre {page-break-after: always;}\n";
387
  print $fh "pre {page-break-after: always;}\n";
418
  print $fh "pre {white-space: pre-wrap;}\n";
388
  print $fh "pre {white-space: pre-wrap;}\n";
Lines 432-816 elsif ( defined $text_filename ) { Link Here
432
  }
402
  }
433
}
403
}
434
404
435
foreach my $branchcode (@branches) {
405
##
436
    if ( C4::Context->preference('OverdueNoticeCalendar') ) {
406
## sql queries
437
        my $calendar = Koha::Calendar->new( branchcode => $branchcode );
407
##
438
        if ( $calendar->is_holiday($date_to_run) ) {
408
my $sqlIssues = "SELECT biblio.*, items.*, issues.*, biblioitems.itemtype, TO_DAYS($date)-TO_DAYS(date_due) AS days_overdue
439
            next;
409
    FROM issues,items,biblio, biblioitems
440
        }
410
    WHERE items.itemnumber=issues.itemnumber
441
    }
442
443
    my $branch_details      = C4::Branch::GetBranchDetail($branchcode);
444
    my $admin_email_address = $branch_details->{'branchemail'}
445
      || C4::Context->preference('KohaAdminEmailAddress');
446
    my @output_chunks;    # may be sent to mail or stdout or csv file.
447
448
    $verbose and warn sprintf "branchcode : '%s' using %s\n", $branchcode, $admin_email_address;
449
450
    my $sth2 = $dbh->prepare( <<"END_SQL" );
451
SELECT biblio.*, items.*, issues.*, biblioitems.itemtype, TO_DAYS($date)-TO_DAYS(date_due) AS days_overdue, branchname
452
  FROM issues,items,biblio, biblioitems, branches b
453
  WHERE items.itemnumber=issues.itemnumber
454
    AND biblio.biblionumber   = items.biblionumber
411
    AND biblio.biblionumber   = items.biblionumber
455
    AND b.branchcode = items.homebranch
456
    AND biblio.biblionumber   = biblioitems.biblionumber
412
    AND biblio.biblionumber   = biblioitems.biblionumber
457
    AND issues.borrowernumber = ?
413
    AND TO_DAYS($date)-TO_DAYS(date_due) BETWEEN 1 AND $MAX ";
458
END_SQL
414
459
415
my $sqlBorrowers = "SELECT issues.borrowernumber, firstname, surname, address, address2, city, zipcode,
460
    my $query = "SELECT * FROM overduerules WHERE delay1 IS NOT NULL AND branchcode = ? ";
416
                           country, email, emailpro, B_email, smsalertnumber, borrowers.categorycode
461
    $query .= " AND categorycode IN (".join( ',' , ('?') x @myborcat ).") " if (@myborcat);
417
    FROM issues,borrowers,categories
462
    $query .= " AND categorycode NOT IN (".join( ',' , ('?') x @myborcatout ).") " if (@myborcatout);
418
    WHERE issues.borrowernumber=borrowers.borrowernumber
463
    
419
    AND borrowers.categorycode=categories.categorycode
464
    my $rqoverduerules =  $dbh->prepare($query);
420
    AND TO_DAYS($date)-TO_DAYS(date_due) BETWEEN 1 AND $MAX
465
    $rqoverduerules->execute($branchcode, @myborcat, @myborcatout);
421
    AND categories.overduenoticerequired=1 ";
466
    
422
467
    # We get default rules is there is no rule for this branch
423
my $sqlRules = "SELECT *
468
    if($rqoverduerules->rows == 0){
424
    FROM overduerules
469
        $query = "SELECT * FROM overduerules WHERE delay1 IS NOT NULL AND branchcode = '' ";
425
    WHERE delay != 0
470
        $query .= " AND categorycode IN (".join( ',' , ('?') x @myborcat ).") " if (@myborcat);
426
    AND letter <> ''
471
        $query .= " AND categorycode NOT IN (".join( ',' , ('?') x @myborcatout ).") " if (@myborcatout);
427
    ORDER BY branchcode desc, categorycode desc, itemtype desc, onhold desc, delay desc";
472
        
428
473
        $rqoverduerules = $dbh->prepare($query);
429
my $sqlMTT = "SELECT message_transport_type from overduerules_transport_types where overduerules_id = ?";
474
        $rqoverduerules->execute(@myborcat, @myborcatout);
430
475
    }
431
my $sqlReserves = "SELECT distinct(biblionumber) FROM reserves";
476
432
477
    # my $outfile = 'overdues_' . ( $mybranch || $branchcode || 'default' );
433
##
478
    while ( my $overdue_rules = $rqoverduerules->fetchrow_hashref ) {
434
## customized depending on parameters
479
      PERIOD: foreach my $i ( 1 .. 3 ) {
435
##
480
436
if(@branchcodes){
481
            $verbose and warn "branch '$branchcode', categorycode = $overdue_rules->{categorycode} pass $i\n";
437
    $sqlIssues .= " AND issues.branchcode IN ('" . join ("','", @branchcodes) . "')";
438
}
439
if(@myborcat){
440
    $sqlBorrowers .= " AND borrower.categorycode IN ('" . join ("','", @myborcat) . "')";
441
}
442
if(@myborcatout){
443
    $sqlBorrowers .= " AND borrower.categorycode NOT IN ('" . join ("','", @myborcatout) . "')";
444
}
482
445
483
            my $mindays = $overdue_rules->{"delay$i"};    # the notice will be sent after mindays days (grace period)
446
##
484
            my $maxdays = (
447
## collect everything in meaningful hashes
485
                  $overdue_rules->{ "delay" . ( $i + 1 ) }
448
##
486
                ? $overdue_rules->{ "delay" . ( $i + 1 ) } - 1
449
my $hrBorrowers = $dbh->selectall_hashref($sqlBorrowers, 'borrowernumber');
487
                : ($MAX)
450
VERBOSE("$sqlBorrowers\nReturned " . (scalar keys $hrBorrowers) . " borrowers with issues.");
488
            );                                            # issues being more than maxdays late are managed somewhere else. (borrower probably suspended)
451
my $hrOverdueIssues = $dbh->selectall_hashref($sqlIssues, 'itemnumber' );
452
VERBOSE("$sqlIssues\nReturned " . (scalar keys $hrOverdueIssues) . " overdue issues.");
453
my $arRules = $dbh->selectall_arrayref($sqlRules, { Slice => {} } );
454
VERBOSE("\nRule order:\nId\tBranch\tCat\tItem\tHold\tDelay\tDebarred\tLetter");
455
foreach my $rule (@$arRules){
456
    $rule->{mtt} = $dbh->selectcol_arrayref($sqlMTT, undef, $rule->{overduerules_id});
457
    VERBOSE("$rule->{id}\t$rule->{branchcode}\t$rule->{categorycode}\t$rule->{itemtype}\t$rule->{onhold}\t$rule->{delay}\t$rule->{debarred}\t$rule->{letter}");
458
}
459
my $hrReserves = $dbh->selectall_hashref($sqlReserves, 'biblionumber');
460
VERBOSE("$sqlReserves\nReturned " . (scalar keys $hrReserves) . " unique reserves.");
461
my %hBranches;
462
my %hChunks; # messages to send once per branch to the administrator
463
464
##
465
## For each issue, determine the most meaningful rule that applies
466
## and associate it to the borrower
467
##
468
469
my $whichItemType = C4::Context->preference('item-level_itypes') ? 'itype' : 'itemtype';
470
foreach my $in (keys $hrOverdueIssues){
471
    my $issue = $hrOverdueIssues->{$in};
472
    VERBOSE("Processing issue $in.  branchcode: $issue->{branchcode}, borrower: $issue->{borrowernumber}.");
473
    foreach my $rule(@$arRules){
474
        if( ($rule->{branchcode} eq '' || $rule->{branchcode} eq $issue->{branchcode}) &&
475
            ($rule->{categorycode} eq '' || $rule->{categorycode} eq $hrBorrowers->{$issue->{borrowernumber}}->{categorycode}) &&
476
            ($rule->{itemtype} eq '' || $rule->{itemtype} eq $issue->{$whichItemType}) &&
477
            (!$rule->{onhold} || defined $hrReserves->{$issue->{biblionumber}})
478
          )
479
        {
480
            # limit the creation of calendar objects
481
            if(!defined $hBranches{$issue->{branchcode}}){
482
                $hBranches{$issue->{branchcode}} = C4::Branch::GetBranchDetail($issue->{branchcode});
483
                $hBranches{$issue->{branchcode}}{CALENDAR} = Koha::Calendar->new( branchcode => $issue->{branchcode} );
484
            }
489
485
490
            next unless defined $mindays;
486
            # validate number of days overdue vs delay
487
            if ( C4::Context->preference('OverdueNoticeCalendar') ){
488
                my $days_between = $hBranches{$issue->{branchcode}}{CALENDAR}->days_between( dt_from_string($issue->{date_due}), $date_to_run );
489
                $issue->{days_overdue} = $days_between->in_units('days');
490
            }
491
            next if ( $triggered && $rule->{delay} != $issue->{days_overdue}); # on "triggered". we only look for exact matches
492
            next if (!$triggered && $rule->{delay} >  $issue->{days_overdue});
491
493
492
            if ( !$overdue_rules->{"letter$i"} ) {
494
            # validate the letter
493
                $verbose and warn "No letter$i code for branch '$branchcode'";
495
            unless(C4::Letters::getletter( 'circulation', $rule->{letter}, $issue->{branchcode} )){
494
                next PERIOD;
496
                VERBOSE("Message '$rule->{letter}' content not found");
497
                last; # if no letter
495
            }
498
            }
496
499
497
            # $letter->{'content'} is the text of the mail that is sent.
500
            # we found the best rule to apply, let's store the branchcode/lettter combo into the borrower
498
            # this text contains fields that are replaced by their value. Those fields must be written between brackets
501
            my $arItems = $hrBorrowers->{$issue->{borrowernumber}}{NOTICES}{$issue->{branchcode}}{$rule->{letter}}{ITEMS} ||= [];
499
            # The following fields are available :
502
            push $arItems, $issue->{itemnumber};
500
	    # 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).
503
            # And note that we union the transport types, so IF the user set two similar alerts (except for delay and mtt), then
501
            # <date> <itemcount> <firstname> <lastname> <address1> <address2> <address3> <city> <postcode> <country>
504
            # only one message will be sent to each of the transport specified by both rules together
502
505
            my $hrMtts = $hrBorrowers->{$issue->{borrowernumber}}{NOTICES}{$issue->{branchcode}}{$rule->{letter}}{MTTS} ||= {};
503
            my $borrower_sql = <<'END_SQL';
506
            foreach my $mtt(@{$rule->{mtt}}){
504
SELECT issues.borrowernumber, firstname, surname, address, address2, city, zipcode, country, email, emailpro, B_email, smsalertnumber, phone, cardnumber,
507
                $hrMtts->{$mtt} = 1;
505
TO_DAYS(?)-TO_DAYS(date_due) as difference, date_due
506
FROM   issues,borrowers,categories
507
WHERE  issues.borrowernumber=borrowers.borrowernumber
508
AND    borrowers.categorycode=categories.categorycode
509
END_SQL
510
            my @borrower_parameters;
511
            push @borrower_parameters, $date_to_run->datetime();
512
            if ($branchcode) {
513
                $borrower_sql .= ' AND issues.branchcode=? ';
514
                push @borrower_parameters, $branchcode;
515
            }
508
            }
516
            if ( $overdue_rules->{categorycode} ) {
509
            VERBOSE("User $hrBorrowers->{$issue->{borrowernumber}}{surname}, Branch $issue->{branchcode}, Letter $rule->{letter} : Item $issue->{itemnumber}");
517
                $borrower_sql .= ' AND borrowers.categorycode=? ';
510
518
                push @borrower_parameters, $overdue_rules->{categorycode};
511
            # block the user if needed
512
            if ( $rule->{"debarred"} ) {
513
                AddUniqueDebarment({
514
                    borrowernumber => $issue->{borrowernumber},
515
                    type           => 'OVERDUES',
516
                    comment => "Restriction added by overdues process " . output_pref( dt_from_string() ),
517
                });
518
                VERBOSE("debarring user $issue->{borrowernumber} (" . $hrBorrowers->{$issue->{borrowernumber}}{surname} . ")");
519
            }
519
            }
520
            $borrower_sql .= '  AND categories.overduenoticerequired=1 ORDER BY issues.borrowernumber';
520
            last; # we found a rule, let's move to the next issue
521
521
        }
522
            # $sth gets borrower info iff at least one overdue item has triggered the overdue action.
522
    }
523
	        my $sth = $dbh->prepare($borrower_sql);
523
}
524
            $sth->execute(@borrower_parameters);
525
526
            $verbose and warn $borrower_sql . "\n $branchcode | " . $overdue_rules->{'categorycode'} . "\n ($mindays, $maxdays, ".  $date_to_run->datetime() .")\nreturns " . $sth->rows . " rows";
527
            my $borrowernumber;
528
            while ( my $data = $sth->fetchrow_hashref ) {
529
530
                next unless ( DateTime->compare( $date_to_run,  dt_from_string($data->{date_due})) ) == 1;
531
532
                # check the borrower has at least one item that matches
533
                my $days_between;
534
                if ( C4::Context->preference('OverdueNoticeCalendar') )
535
                {
536
                    my $calendar =
537
                      Koha::Calendar->new( branchcode => $branchcode );
538
                    $days_between =
539
                      $calendar->days_between(  dt_from_string($data->{date_due}),
540
                        $date_to_run );
541
                }
542
                else {
543
                    $days_between =
544
                      $date_to_run->delta_days(  dt_from_string($data->{date_due}) );
545
                }
546
                $days_between = $days_between->in_units('days');
547
                if ($triggered) {
548
                    if ( $mindays != $days_between ) {
549
                        next;
550
                    }
551
                }
552
                else {
553
                    unless (   $days_between >= $mindays
554
                        && $days_between <= $maxdays )
555
                    {
556
                        next;
557
                    }
558
                }
559
                if (defined $borrowernumber && $borrowernumber eq $data->{'borrowernumber'}){
560
# we have already dealt with this borrower
561
                    $verbose and warn "already dealt with this borrower $borrowernumber";
562
                    next;
563
                }
564
                $borrowernumber = $data->{'borrowernumber'};
565
                my $borr =
566
                    $data->{'firstname'} . ', '
567
                  . $data->{'surname'} . ' ('
568
                  . $borrowernumber . ')';
569
                $verbose
570
                  and warn "borrower $borr has items triggering level $i.";
571
572
                @emails_to_use = ();
573
                my $notice_email =
574
                    C4::Members::GetNoticeEmailAddress($borrowernumber);
575
                unless ($nomail) {
576
                    if (@emails) {
577
                        foreach (@emails) {
578
                            push @emails_to_use, $data->{$_} if ( $data->{$_} );
579
                        }
580
                    }
581
                    else {
582
                        push @emails_to_use, $notice_email if ($notice_email);
583
                    }
584
                }
585
524
586
                my $letter = C4::Letters::getletter( 'circulation', $overdue_rules->{"letter$i"}, $branchcode );
587
525
588
                unless ($letter) {
526
##
589
                    $verbose and warn qq|Message '$overdue_rules->{"letter$i"}' content not found|;
527
## Main loop
528
##
529
VERBOSE("Main loop.");
530
foreach my $borrowernumber(keys $hrBorrowers){
531
    if(!defined $hrBorrowers->{$borrowernumber}{NOTICES}){
532
        VERBOSE("User $hrBorrowers->{$borrowernumber}{surname} has no notice.");
533
        next;
534
    }
535
    # for each branch
536
    foreach my $branchcode (keys $hrBorrowers->{$borrowernumber}{NOTICES}){
537
        my $admin_email_address = $hBranches{$branchcode}{'branchemail'} || C4::Context->preference('KohaAdminEmailAddress');
538
        if($hBranches{$branchcode}{CALENDAR}->is_holiday($date_to_run)){
539
            VERBOSE("$date_to_run is a holiday at branch $branchcode");
540
            next;
541
        }
590
542
591
                    # might as well skip while PERIOD, no other borrowers are going to work.
543
        # for each letter
592
                    # FIXME : Does this mean a letter must be defined in order to trigger a debar ?
544
        foreach my $lettercode (keys $hrBorrowers->{$borrowernumber}{NOTICES}{$branchcode}){
593
                    next PERIOD;
545
            @emails_to_use = ();
546
            my $notice_email = C4::Members::GetNoticeEmailAddress($borrowernumber);
547
            unless ($nomail) {
548
                if (@emails) {
549
                    @emails_to_use = @emails;
550
  #              foreach (@emails) {
551
#                    push @emails_to_use, $data->{$_} if ( $data->{$_} );
552
 #               }
553
                }else{
554
                    push @emails_to_use, $notice_email if ($notice_email);
594
                }
555
                }
595
    
556
            }
596
                if ( $overdue_rules->{"debarred$i"} ) {
557
597
    
558
            # finally, the items
598
                    #action taken is debarring
559
            my $arItems = $hrBorrowers->{$borrowernumber}{NOTICES}{$branchcode}{$lettercode}{ITEMS};
599
                    AddUniqueDebarment(
560
            if(!defined $arItems){
600
                        {
561
                VERBOSE("No item for borrower $borrowernumber");
601
                            borrowernumber => $borrowernumber,
562
                next;
602
                            type           => 'OVERDUES',
563
            }
603
                            comment => "Restriction added by overdues process "
564
            my $itemcount = 0;
604
                              . output_pref( dt_from_string() ),
565
            my $titles = "";
605
                        }
566
            my @items = ();
606
                    );
567
607
                    $verbose and warn "debarring $borr\n";
568
            my $j = 0;
569
            my $exceededPrintNoticesMaxLines = 0;
570
            foreach my $itemnumber(@{$arItems}){
571
                if ( ( scalar(@emails_to_use) == 0 || $nomail ) && $printNoticesMaxLines && $j >= $printNoticesMaxLines ) {
572
                    $exceededPrintNoticesMaxLines = 1;
573
                    last;
608
                }
574
                }
609
                my @params = ($borrowernumber);
575
                $j++;
610
                $verbose and warn "STH2 PARAMS: borrowernumber = $borrowernumber";
576
                my $item = $hrOverdueIssues->{$itemnumber};
611
577
                my @item_info = map { $_ =~ /^date|date$/ ? format_date( $item->{$_} ) : $item->{$_} || '' } @item_content_fields;
612
                $sth2->execute(@params);
578
                $titles .= join("\t", @item_info) . "\n";
613
                my $itemcount = 0;
579
                $itemcount++;
614
                my $titles = "";
580
                push @items, $item;
615
                my @items = ();
581
            }
616
                
617
                my $j = 0;
618
                my $exceededPrintNoticesMaxLines = 0;
619
                while ( my $item_info = $sth2->fetchrow_hashref() ) {
620
                    next unless ( DateTime->compare( $date_to_run,  dt_from_string($item_info->{date_due})) ) == 1;
621
622
                    if ( C4::Context->preference('OverdueNoticeCalendar') ) {
623
                        my $calendar =
624
                          Koha::Calendar->new( branchcode => $branchcode );
625
                        $days_between =
626
                          $calendar->days_between(
627
                            dt_from_string( $item_info->{date_due} ), $date_to_run );
628
                    }
629
                    else {
630
                        $days_between =
631
                          $date_to_run->delta_days(
632
                            dt_from_string( $item_info->{date_due} ) );
633
                    }
634
                    $days_between = $days_between->in_units('days');
635
                    if ($listall){
636
                        unless ($days_between >= 1 and $days_between <= $MAX){
637
                            next;
638
                        }
639
                    }
640
                    else {
641
                        if ($triggered) {
642
                            if ( $mindays != $days_between ) {
643
                                next;
644
                            }
645
                        }
646
                        else {
647
                            unless ( $days_between >= $mindays
648
                                && $days_between <= $maxdays )
649
                            {
650
                                next;
651
                            }
652
                        }
653
                    }
654
582
655
                    if ( ( scalar(@emails_to_use) == 0 || $nomail ) && $PrintNoticesMaxLines && $j >= $PrintNoticesMaxLines ) {
583
            # the transport types (email, sms...)
656
                      $exceededPrintNoticesMaxLines = 1;
584
            my @message_transport_types = keys $hrBorrowers->{$borrowernumber}{NOTICES}{$branchcode}{$lettercode}{MTTS};
657
                      last;
585
            my $print_sent = 0; # A print notice is not yet sent for this patron
658
                    }
586
            for my $mtt ( @message_transport_types ) {
659
                    $j++;
587
                my $letter = parse_letter( {
660
                    my @item_info = map { $_ =~ /^date|date$/ ? format_date( $item_info->{$_} ) : $item_info->{$_} || '' } @item_content_fields;
588
                    letter_code     => $lettercode,
661
                    $titles .= join("\t", @item_info) . "\n";
589
                    borrowernumber  => $borrowernumber,
662
                    $itemcount++;
590
                    branchcode      => $branchcode,
663
                    push @items, $item_info;
591
                    items           => \@items,
592
                    substitute      => {    # this appears to be a hack to overcome incomplete features in this code.
593
                                        bib             => $hBranches{$branchcode}{'branchname'}, # maybe 'bib' is a typo for 'lib<rary>'?
594
                                        'items.content' => $titles,
595
                                        'count'         => $itemcount,
596
                                       },
597
                    message_transport_type => $mtt,
598
                } );
599
                unless ($letter) {
600
                    VERBOSE("Message '$lettercode' content not found (transport $mtt, branch $branchcode)");
601
                    next;
664
                }
602
                }
665
                $sth2->finish;
666
667
                my @message_transport_types = @{ GetOverdueMessageTransportTypes( $branchcode, $overdue_rules->{categorycode}, $i) };
668
                @message_transport_types = @{ GetOverdueMessageTransportTypes( q{}, $overdue_rules->{categorycode}, $i) }
669
                    unless @message_transport_types;
670
671
672
                my $print_sent = 0; # A print notice is not yet sent for this patron
673
                for my $mtt ( @message_transport_types ) {
674
675
                    my $letter = parse_letter(
676
                        {   letter_code     => $overdue_rules->{"letter$i"},
677
                            borrowernumber  => $borrowernumber,
678
                            branchcode      => $branchcode,
679
                            items           => \@items,
680
                            substitute      => {    # this appears to be a hack to overcome incomplete features in this code.
681
                                                bib             => $branch_details->{'branchname'}, # maybe 'bib' is a typo for 'lib<rary>'?
682
                                                'items.content' => $titles,
683
                                                'count'         => $itemcount,
684
                                               },
685
                            message_transport_type => $mtt,
686
                        }
687
                    );
688
                    unless ($letter) {
689
                        $verbose and warn qq|Message '$overdue_rules->{"letter$i"}' content not found|;
690
                        # this transport doesn't have a configured notice, so try another
691
                        next;
692
                    }
693
603
694
                    if ( $exceededPrintNoticesMaxLines ) {
604
                if ( $exceededPrintNoticesMaxLines ) {
695
                      $letter->{'content'} .= "List too long for form; please check your account online for a complete list of your overdue items.";
605
                    $letter->{'content'} .= "List too long for form; please check your account online for a complete list of your overdue items.";
696
                    }
606
                }
697
607
698
                    my @misses = grep { /./ } map { /^([^>]*)[>]+/; ( $1 || '' ); } split /\</, $letter->{'content'};
608
                my @misses = grep { /./ } map { /^([^>]*)[>]+/; ( $1 || '' ); } split /\</, $letter->{'content'};
699
                    if (@misses) {
609
                if (@misses) {
700
                        $verbose and warn "The following terms were not matched and replaced: \n\t" . join "\n\t", @misses;
610
                    VERBOSE( "The following terms were not matched and replaced: \n\t" . join "\n\t", @misses );
701
                    }
611
                }
702
612
703
                    if ($nomail) {
613
                if ($nomail) {
704
                        push @output_chunks,
614
                    $hChunks{$admin_email_address} ||= [];
705
                          prepare_letter_for_printing(
615
                    push $hChunks{$admin_email_address}, prepare_letter_for_printing( {
706
                          {   letter         => $letter,
616
                        letter         => $letter,
707
                              borrowernumber => $borrowernumber,
617
                        borrowernumber => $borrowernumber,
708
                              firstname      => $data->{'firstname'},
618
                        firstname      => $hrBorrowers->{$borrowernumber}->{'firstname'},
709
                              lastname       => $data->{'surname'},
619
                        lastname       => $hrBorrowers->{$borrowernumber}->{'surname'},
710
                              address1       => $data->{'address'},
620
                        address1       => $hrBorrowers->{$borrowernumber}->{'address'},
711
                              address2       => $data->{'address2'},
621
                        address2       => $hrBorrowers->{$borrowernumber}->{'address2'},
712
                              city           => $data->{'city'},
622
                        city           => $hrBorrowers->{$borrowernumber}->{'city'},
713
                              phone          => $data->{'phone'},
623
                        postcode       => $hrBorrowers->{$borrowernumber}->{'zipcode'},
714
                              cardnumber     => $data->{'cardnumber'},
624
                        country        => $hrBorrowers->{$borrowernumber}->{'country'},
715
                              branchname     => $branch_details->{'branchname'},
625
                        email          => $notice_email,
716
                              letternumber   => $i,
626
                        itemcount      => $itemcount,
717
                              postcode       => $data->{'zipcode'},
627
                        titles         => $titles,
718
                              country        => $data->{'country'},
628
                        outputformat   => defined $csvfilename ? 'csv' : defined $htmlfilename ? 'html' : defined $text_filename ? 'text' : '',
719
                              email          => $notice_email,
629
                    } );
720
                              itemcount      => $itemcount,
630
                } else {
721
                              titles         => $titles,
631
                    if ( ($mtt eq 'email' and not scalar @emails_to_use) or ($mtt eq 'sms' and not $hrBorrowers->{$borrowernumber}->{smsalertnumber}) ) {
722
                              outputformat   => defined $csvfilename ? 'csv' : defined $htmlfilename ? 'html' : defined $text_filename ? 'text' : '',
632
                        # email or sms is requested but not exist, do a print.
633
                        $mtt = 'print';
634
                    }
635
                    unless ( $mtt eq 'print' and $print_sent == 1 ) {
636
                        # Just sent a print if not already done.
637
                        C4::Letters::EnqueueLetter(
638
                            {   letter                 => $letter,
639
                                borrowernumber         => $borrowernumber,
640
                                message_transport_type => $mtt,
641
                                from_address           => $admin_email_address,
642
                                to_address             => join(',', @emails_to_use),
723
                            }
643
                            }
724
                          );
644
                        );
725
                    } else {
645
                        # A print notice should be sent only once per overdue level.
726
                        if ( ($mtt eq 'email' and not scalar @emails_to_use) or ($mtt eq 'sms' and not $data->{smsalertnumber}) ) {
646
                        # 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.
727
                            # email or sms is requested but not exist, do a print.
647
                        $print_sent = 1 if $mtt eq 'print';
728
                            $mtt = 'print';
729
                            push @output_chunks,
730
                              prepare_letter_for_printing(
731
                              {   letter         => $letter,
732
                                  borrowernumber => $borrowernumber,
733
                                  firstname      => $data->{'firstname'},
734
                                  lastname       => $data->{'surname'},
735
                                  address1       => $data->{'address'},
736
                                  address2       => $data->{'address2'},
737
                                  city           => $data->{'city'},
738
                                  postcode       => $data->{'zipcode'},
739
                                  country        => $data->{'country'},
740
                                  email          => $notice_email,
741
                                  itemcount      => $itemcount,
742
                                  titles         => $titles,
743
                                  outputformat   => defined $csvfilename ? 'csv' : defined $htmlfilename ? 'html' : defined $text_filename ? 'text' : '',
744
                                }
745
                              );
746
                        }
747
                        unless ( $mtt eq 'print' and $print_sent == 1 ) {
748
                            # Just sent a print if not already done.
749
                            C4::Letters::EnqueueLetter(
750
                                {   letter                 => $letter,
751
                                    borrowernumber         => $borrowernumber,
752
                                    message_transport_type => $mtt,
753
                                    from_address           => $admin_email_address,
754
                                    to_address             => join(',', @emails_to_use),
755
                                }
756
                            );
757
                            # A print notice should be sent only once per overdue level.
758
                            # 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.
759
                            $print_sent = 1 if $mtt eq 'print';
760
                        }
761
                    }
648
                    }
762
                }
649
                }
763
            }
650
            }
764
            $sth->finish;
765
        }
651
        }
766
    }
652
    }
653
}
767
654
768
    if (@output_chunks) {
655
foreach my $admin_email_address (keys %hChunks) {
769
        if ( defined $csvfilename ) {
656
    my @output_chunks = @{$hChunks{$admin_email_address}};
770
            print $csv_fh @output_chunks;        
657
    if ( defined $csvfilename ) {
771
        }
658
        print $csv_fh @output_chunks;
772
        elsif ( defined $htmlfilename ) {
659
    }
773
            print $fh @output_chunks;        
660
    elsif ( defined $htmlfilename ) {
774
        }
661
        print $fh @output_chunks;
775
        elsif ( defined $text_filename ) {
662
    }
776
            print $fh @output_chunks;        
663
    elsif ( defined $text_filename ) {
777
        }
664
        print $fh @output_chunks;
778
        elsif ($nomail){
665
    }
779
                local $, = "\f";    # pagebreak
666
    elsif ($nomail){
780
                print @output_chunks;
667
        local $, = "\f";    # pagebreak
781
        }
668
        print @output_chunks;
782
        # Generate the content of the csv with headers
669
    }
783
        my $content;
670
    # Generate the content of the csv with headers
784
        if ( defined $csvfilename ) {
671
    my $content;
785
            my $delimiter = C4::Context->preference('delimiter') || ';';
672
    if ( defined $csvfilename ) {
786
            $content = join($delimiter, qw(title name surname address1 address2 zipcode city country email itemcount itemsinfo due_date issue_date)) . "\n";
673
        my $delimiter = C4::Context->preference('delimiter') || ';';
787
        }
674
        $content = join($delimiter, qw(title name surname address1 address2 zipcode city country email itemcount itemsinfo due_date issue_date)) . "\n";
788
        else {
789
            $content = "";
790
        }
791
        $content .= join( "\n", @output_chunks );
792
793
        my $attachment = {
794
            filename => defined $csvfilename ? 'attachment.csv' : 'attachment.txt',
795
            type => 'text/plain',
796
            content => $content, 
797
        };
798
799
        my $letter = {
800
            title   => 'Overdue Notices',
801
            content => 'These messages were not sent directly to the patrons.',
802
        };
803
        C4::Letters::EnqueueLetter(
804
            {   letter                 => $letter,
805
                borrowernumber         => undef,
806
                message_transport_type => 'email',
807
                attachments            => [$attachment],
808
                to_address             => $admin_email_address,
809
            }
810
        );
811
    }
675
    }
676
    else {
677
        $content = "";
678
    }
679
    $content .= join( "\n", @output_chunks );
680
681
    my $attachment = {
682
        filename => defined $csvfilename ? 'attachment.csv' : 'attachment.txt',
683
        type => 'text/plain',
684
        content => $content,
685
    };
812
686
687
    my $letter = {
688
        title   => 'Overdue Notices',
689
        content => 'These messages were not sent directly to the patrons.',
690
    };
691
    C4::Letters::EnqueueLetter( {
692
        letter                 => $letter,
693
        borrowernumber         => undef,
694
        message_transport_type => 'email',
695
        attachments            => [$attachment],
696
        to_address             => $admin_email_address,
697
    } );
813
}
698
}
699
814
if ($csvfilename) {
700
if ($csvfilename) {
815
    # note that we're not testing on $csv_fh to prevent closing
701
    # note that we're not testing on $csv_fh to prevent closing
816
    # STDOUT.
702
    # STDOUT.
Lines 818-828 if ($csvfilename) { Link Here
818
}
704
}
819
705
820
if ( defined $htmlfilename ) {
706
if ( defined $htmlfilename ) {
821
  print $fh "</body>\n";
707
    print $fh "</body>\n";
822
  print $fh "</html>\n";
708
    print $fh "</html>\n";
823
  close $fh;
709
    close $fh;
824
} elsif ( defined $text_filename ) {
710
} elsif ( defined $text_filename ) {
825
  close $fh;
711
    close $fh;
826
}
712
}
827
713
828
=head1 INTERNAL METHODS
714
=head1 INTERNAL METHODS
Lines 928-935 sub prepare_letter_for_printing { Link Here
928
    if ( exists $params->{'outputformat'} && $params->{'outputformat'} eq 'csv' ) {
814
    if ( exists $params->{'outputformat'} && $params->{'outputformat'} eq 'csv' ) {
929
        if ($csv->combine(
815
        if ($csv->combine(
930
                $params->{'firstname'}, $params->{'lastname'}, $params->{'address1'},  $params->{'address2'}, $params->{'postcode'},
816
                $params->{'firstname'}, $params->{'lastname'}, $params->{'address1'},  $params->{'address2'}, $params->{'postcode'},
931
                $params->{'city'}, $params->{'country'}, $params->{'email'}, $params->{'phone'}, $params->{'cardnumber'},
817
                $params->{'city'}, $params->{'country'}, $params->{'email'}, $params->{'itemcount'}, $params->{'titles'}
932
                $params->{'itemcount'}, $params->{'titles'}, $params->{'branchname'}, $params->{'letternumber'}
933
            )
818
            )
934
          ) {
819
          ) {
935
            return $csv->string, "\n";
820
            return $csv->string, "\n";
936
- 

Return to bug 12772