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

(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref (+18 lines)
Lines 445-450 Circulation: Link Here
445
            - "."
445
            - "."
446
            - <br>Leave this field empty if you don't want to skip any <a href="/cgi-bin/koha/admin/authorised_values.pl?searchfield=LOST">LOST</a> statuses.
446
            - <br>Leave this field empty if you don't want to skip any <a href="/cgi-bin/koha/admin/authorised_values.pl?searchfield=LOST">LOST</a> statuses.
447
            - <br>Set to a list of comma separated values, e.g. <em>5,6,7</em>.
447
            - <br>Set to a list of comma separated values, e.g. <em>5,6,7</em>.
448
449
        -
450
            - pref: DefaultLongOverdueBorrowerCategories
451
              choices: patron-categories
452
              class: multiple
453
            - the borrower categories that will be listed. Exclude all others.
454
            - <br>WARNING — This preference will be active only if DefaultLongOverdueSkipBorrowerCategories is empty.
455
            - "<br><strong>NOTE:</strong> This system preference requires the <code>misc/cronjobs/longoverdue.pl</code> cronjob. Ask your system administrator to schedule it."
456
457
458
        -   
459
            - pref: DefaultLongOverdueSkipBorrowerCategories
460
              choices: patron-categories
461
              class: multiple
462
            - the borrower categories that will not be listed. Include all others.
463
            - <br>WARNING - This preference overrides the DefaultLongOverdueBorrowerCategories preference, so the DefaultLongOverdueBorrowerCategories filter will not take effect.
464
            - "<br><strong>NOTE:</strong> This system preference requires the <code>misc/cronjobs/longoverdue.pl</code> cronjob. Ask your system administrator to schedule it."
465
448
        -
466
        -
449
            - "When issuing an item that has been marked as lost, "
467
            - "When issuing an item that has been marked as lost, "
450
            - pref: IssueLostItem
468
            - pref: IssueLostItem
(-)a/misc/cronjobs/longoverdue.pl (-92 / +112 lines)
Lines 39-59 use Koha::Patron::Categories; Link Here
39
use Koha::Patrons;
39
use Koha::Patrons;
40
use Koha::Script -cron;
40
use Koha::Script -cron;
41
41
42
my  $lost;  #  key=lost value,  value=num days.
42
my $lost;    #  key=lost value,  value=num days.
43
my ($charge, $verbose, $confirm, $quiet);
43
my ( $charge, $verbose, $confirm, $quiet );
44
my $endrange = 366;
44
my $endrange = 366;
45
my $mark_returned;
45
my $mark_returned;
46
my $borrower_category = [];
46
my $borrower_category      = [];
47
my $skip_borrower_category = [];
47
my $skip_borrower_category = [];
48
my $itemtype = [];
48
my $itemtype               = [];
49
my $skip_itemtype = [];
49
my $skip_itemtype          = [];
50
my $help=0;
50
my $help                   = 0;
51
my $man=0;
51
my $man                    = 0;
52
my $list_categories = 0;
52
my $list_categories        = 0;
53
my $list_itemtypes = 0;
53
my $list_itemtypes         = 0;
54
my @skip_lost_values;
54
my @skip_lost_values;
55
55
56
my $command_line_options = join(" ",@ARGV);
56
my $command_line_options = join( " ", @ARGV );
57
57
58
GetOptions(
58
GetOptions(
59
    'l|lost=s%'         => \$lost,
59
    'l|lost=s%'         => \$lost,
Lines 74-115 GetOptions( Link Here
74
    'skip-lost-value=s' => \@skip_lost_values,
74
    'skip-lost-value=s' => \@skip_lost_values,
75
);
75
);
76
76
77
if ( $man ) {
77
if ($man) {
78
    pod2usage( -verbose => 2
78
    pod2usage( -verbose => 2 -exitval => 0 );
79
               -exitval => 0
80
            );
81
}
79
}
82
80
83
if ( $help ) {
81
if ($help) {
84
    pod2usage( -verbose => 1,
82
    pod2usage(
85
               -exitval => 0
83
        -verbose => 1,
86
            );
84
        -exitval => 0
85
    );
87
}
86
}
88
87
89
if ( scalar @$borrower_category && scalar @$skip_borrower_category) {
88
if ( scalar @$borrower_category && scalar @$skip_borrower_category ) {
90
    pod2usage( -verbose => 1,
89
    pod2usage(
91
               -message => "The options --category and --skip-category are mutually exclusive.\n"
90
        -verbose => 1,
92
                           . "Use one or the other.",
91
        -message => "The options --category and --skip-category are mutually exclusive.\n" . "Use one or the other.",
93
               -exitval => 1
92
        -exitval => 1
94
            );
93
    );
95
}
94
}
96
95
97
if ( scalar @$itemtype && scalar @$skip_itemtype) {
96
if ( scalar @$itemtype && scalar @$skip_itemtype ) {
98
    pod2usage( -verbose => 1,
97
    pod2usage(
99
               -message => "The options --itemtype and --skip-itemtype are mutually exclusive.\n"
98
        -verbose => 1,
100
                           . "Use one or the other.",
99
        -message => "The options --itemtype and --skip-itemtype are mutually exclusive.\n" . "Use one or the other.",
101
               -exitval => 1
100
        -exitval => 1
102
            );
101
    );
103
}
102
}
104
103
105
if ( $list_categories ) {
104
if ($list_categories) {
106
105
107
    my @categories = Koha::Patron::Categories->search()->get_column('categorycode');
106
    my @categories = Koha::Patron::Categories->search()->get_column('categorycode');
108
    print "\nBorrower Categories: " . join( " ", @categories ) . "\n\n";
107
    print "\nBorrower Categories: " . join( " ", @categories ) . "\n\n";
109
    exit 0;
108
    exit 0;
110
}
109
}
111
110
112
if ( $list_itemtypes ) {
111
if ($list_itemtypes) {
113
    my @itemtypes = Koha::ItemTypes->search()->get_column('itemtype');
112
    my @itemtypes = Koha::ItemTypes->search()->get_column('itemtype');
114
    print "\nItemtypes: " . join( " ", @itemtypes ) . "\n\n";
113
    print "\nItemtypes: " . join( " ", @itemtypes ) . "\n\n";
115
    exit 0;
114
    exit 0;
Lines 252-286 near-term release, so this script is not intended to have a long lifetime. Link Here
252
#
251
#
253
252
254
unless ( scalar @skip_lost_values ) {
253
unless ( scalar @skip_lost_values ) {
255
    my $preference = C4::Context->preference( 'DefaultLongOverdueSkipLostStatuses' );
254
    my $preference = C4::Context->preference('DefaultLongOverdueSkipLostStatuses');
256
    @skip_lost_values = split( ',', $preference );
255
    @skip_lost_values = split( ',', $preference );
257
}
256
}
258
257
259
if ( ! defined($lost) ) {
258
if ( !defined($lost) ) {
260
    my $longoverdue_value = C4::Context->preference('DefaultLongOverdueLostValue');
259
    my $longoverdue_value = C4::Context->preference('DefaultLongOverdueLostValue');
261
    my $longoverdue_days = C4::Context->preference('DefaultLongOverdueDays');
260
    my $longoverdue_days  = C4::Context->preference('DefaultLongOverdueDays');
262
    if(defined($longoverdue_value) and defined($longoverdue_days) and $longoverdue_value ne '' and $longoverdue_days ne '' and $longoverdue_days >= 0) {
261
    if (    defined($longoverdue_value)
262
        and defined($longoverdue_days)
263
        and $longoverdue_value ne ''
264
        and $longoverdue_days ne ''
265
        and $longoverdue_days >= 0 )
266
    {
263
        $lost->{$longoverdue_days} = $longoverdue_value;
267
        $lost->{$longoverdue_days} = $longoverdue_value;
264
    }
268
    } else {
265
    else {
269
        pod2usage(
266
        pod2usage( {
270
            {
267
                -exitval => 1,
271
                -exitval => 1,
268
                -msg => q|ERROR: No --lost (-l) option or system preferences DefaultLongOverdueLostValue/DefaultLongOverdueDays defined|,
272
                -msg     =>
269
        } );
273
                    q|ERROR: No --lost (-l) option or system preferences DefaultLongOverdueLostValue/DefaultLongOverdueDays defined|,
274
            }
275
        );
270
    }
276
    }
271
}
277
}
272
if ( ! defined($charge) ) {
278
279
if ( !defined($charge) ) {
273
    my $charge_value = C4::Context->preference('DefaultLongOverdueChargeValue');
280
    my $charge_value = C4::Context->preference('DefaultLongOverdueChargeValue');
274
    if(defined($charge_value) and $charge_value ne '') {
281
    if ( defined($charge_value) and $charge_value ne '' ) {
275
        $charge = $charge_value;
282
        $charge = $charge_value;
276
    }
283
    }
277
}
284
}
285
286
if ( C4::Context->preference('DefaultLongOverdueBorrowerCategories') ) {
287
    my $categories = C4::Context->preference('DefaultLongOverdueBorrowerCategories');
288
    $borrower_category = [ split( ',', $categories ) ];
289
}
290
291
if ( C4::Context->preference('DefaultLongOverdueSkipBorrowerCategories') ) {
292
    my $categories = C4::Context->preference('DefaultLongOverdueSkipBorrowerCategories');
293
    $skip_borrower_category = [ split( ',', $categories ) ];
294
}
295
278
unless ($confirm) {
296
unless ($confirm) {
279
    $verbose = 1;     # If you're not running it for real, then the whole point is the print output.
297
    $verbose = 1;    # If you're not running it for real, then the whole point is the print output.
280
    print "### TEST MODE -- NO ACTIONS TAKEN ###\n";
298
    print "### TEST MODE -- NO ACTIONS TAKEN ###\n";
281
}
299
}
282
300
283
cronlogaction({ info => $command_line_options });
301
cronlogaction( { info => $command_line_options } );
284
302
285
# In my opinion, this line is safe SQL to have outside the API. --atz
303
# In my opinion, this line is safe SQL to have outside the API. --atz
286
our $bounds_sth = C4::Context->dbh->prepare("SELECT DATE_SUB(CURDATE(), INTERVAL ? DAY)");
304
our $bounds_sth = C4::Context->dbh->prepare("SELECT DATE_SUB(CURDATE(), INTERVAL ? DAY)");
Lines 292-304 sub bounds { Link Here
292
310
293
# FIXME - This sql should be inside the API.
311
# FIXME - This sql should be inside the API.
294
sub longoverdue_sth {
312
sub longoverdue_sth {
295
    my ( $params ) = @_;
313
    my ($params) = @_;
296
    my $skip_lost_values = $params->{skip_lost_values};
314
    my $skip_lost_values = $params->{skip_lost_values};
297
315
298
    my $skip_lost_values_sql = q{};
316
    my $skip_lost_values_sql = q{};
299
    if ( @$skip_lost_values ) {
317
    if (@$skip_lost_values) {
300
        my $values = join( ',', map { qq{'$_'} } @$skip_lost_values );
318
        my $values = join( ',', map { qq{'$_'} } @$skip_lost_values );
301
        $skip_lost_values_sql = "AND itemlost NOT IN ( $values )"
319
        $skip_lost_values_sql = "AND itemlost NOT IN ( $values )";
302
    }
320
    }
303
321
304
    my $query = "
322
    my $query = "
Lines 317-326 sub longoverdue_sth { Link Here
317
my $dbh = C4::Context->dbh;
335
my $dbh = C4::Context->dbh;
318
336
319
my @available_categories = Koha::Patron::Categories->search()->get_column('categorycode');
337
my @available_categories = Koha::Patron::Categories->search()->get_column('categorycode');
320
$borrower_category = [ map { uc $_ } @$borrower_category ];
338
$borrower_category      = [ map { uc $_ } @$borrower_category ];
321
$skip_borrower_category = [ map { uc $_} @$skip_borrower_category ];
339
$skip_borrower_category = [ map { uc $_ } @$skip_borrower_category ];
322
my %category_to_process;
340
my %category_to_process;
323
for my $cat ( @$borrower_category ) {
341
for my $cat (@$borrower_category) {
324
    unless ( grep { $_ eq $cat } @available_categories ) {
342
    unless ( grep { $_ eq $cat } @available_categories ) {
325
        pod2usage(
343
        pod2usage(
326
            '-exitval' => 1,
344
            '-exitval' => 1,
Lines 329-336 for my $cat ( @$borrower_category ) { Link Here
329
    }
347
    }
330
    $category_to_process{$cat} = 1;
348
    $category_to_process{$cat} = 1;
331
}
349
}
332
if ( @$skip_borrower_category ) {
350
if (@$skip_borrower_category) {
333
    for my $cat ( @$skip_borrower_category ) {
351
    for my $cat (@$skip_borrower_category) {
334
        unless ( grep { $_ eq $cat } @available_categories ) {
352
        unless ( grep { $_ eq $cat } @available_categories ) {
335
            pod2usage(
353
            pod2usage(
336
                '-exitval' => 1,
354
                '-exitval' => 1,
Lines 345-354 if ( @$skip_borrower_category ) { Link Here
345
my $filter_borrower_categories = ( scalar @$borrower_category || scalar @$skip_borrower_category );
363
my $filter_borrower_categories = ( scalar @$borrower_category || scalar @$skip_borrower_category );
346
364
347
my @available_itemtypes = Koha::ItemTypes->search()->get_column('itemtype');
365
my @available_itemtypes = Koha::ItemTypes->search()->get_column('itemtype');
348
$itemtype = [ map { uc $_ } @$itemtype ];
366
$itemtype      = [ map { uc $_ } @$itemtype ];
349
$skip_itemtype = [ map { uc $_} @$skip_itemtype ];
367
$skip_itemtype = [ map { uc $_ } @$skip_itemtype ];
350
my %itemtype_to_process;
368
my %itemtype_to_process;
351
for my $it ( @$itemtype ) {
369
for my $it (@$itemtype) {
352
    unless ( grep { $_ eq $it } @available_itemtypes ) {
370
    unless ( grep { $_ eq $it } @available_itemtypes ) {
353
        pod2usage(
371
        pod2usage(
354
            '-exitval' => 1,
372
            '-exitval' => 1,
Lines 357-364 for my $it ( @$itemtype ) { Link Here
357
    }
375
    }
358
    $itemtype_to_process{$it} = 1;
376
    $itemtype_to_process{$it} = 1;
359
}
377
}
360
if ( @$skip_itemtype ) {
378
if (@$skip_itemtype) {
361
    for my $it ( @$skip_itemtype ) {
379
    for my $it (@$skip_itemtype) {
362
        unless ( grep { $_ eq $it } @available_itemtypes ) {
380
        unless ( grep { $_ eq $it } @available_itemtypes ) {
363
            pod2usage(
381
            pod2usage(
364
                '-exitval' => 1,
382
                '-exitval' => 1,
Lines 375-427 my $filter_itemtypes = ( scalar @$itemtype || scalar @$skip_itemtype ); Link Here
375
my $count;
393
my $count;
376
my @report;
394
my @report;
377
my $total = 0;
395
my $total = 0;
378
my $i = 0;
396
my $i     = 0;
379
397
380
# FIXME - The item is only marked returned if you supply --charge .
398
# FIXME - The item is only marked returned if you supply --charge .
381
#         We need a better way to handle this.
399
#         We need a better way to handle this.
382
#
400
#
383
my $sth_items = longoverdue_sth({ skip_lost_values => \@skip_lost_values });
401
my $sth_items = longoverdue_sth( { skip_lost_values => \@skip_lost_values } );
384
402
385
foreach my $startrange (sort keys %$lost) {
403
foreach my $startrange ( sort keys %$lost ) {
386
    if( my $lostvalue = $lost->{$startrange} ) {
404
    if ( my $lostvalue = $lost->{$startrange} ) {
387
        my ($date1) = bounds($startrange);
405
        my ($date1) = bounds($startrange);
388
        my ($date2) = bounds(  $endrange);
406
        my ($date2) = bounds($endrange);
407
389
        # print "\nRange ", ++$i, "\nDue $startrange - $endrange days ago ($date2 to $date1), lost => $lostvalue\n" if($verbose);
408
        # print "\nRange ", ++$i, "\nDue $startrange - $endrange days ago ($date2 to $date1), lost => $lostvalue\n" if($verbose);
390
        $verbose and
409
        $verbose
391
            printf "\nRange %s\nDue %3s - %3s days ago (%s to %s), lost => %s\n", ++$i,
410
            and printf "\nRange %s\nDue %3s - %3s days ago (%s to %s), lost => %s\n", ++$i,
392
            $startrange, $endrange, $date2, $date1, $lostvalue;
411
            $startrange, $endrange, $date2, $date1, $lostvalue;
393
        $sth_items->execute($startrange, $endrange, $lostvalue);
412
        $sth_items->execute( $startrange, $endrange, $lostvalue );
394
        $count=0;
413
        $count = 0;
395
        ITEM: while (my $row=$sth_items->fetchrow_hashref) {
414
    ITEM: while ( my $row = $sth_items->fetchrow_hashref ) {
396
            if( $filter_borrower_categories ) {
415
            if ($filter_borrower_categories) {
397
                my $category = uc Koha::Patrons->find( $row->{borrowernumber} )->categorycode();
416
                my $category = uc Koha::Patrons->find( $row->{borrowernumber} )->categorycode();
398
                next ITEM unless ( $category_to_process{ $category } );
417
                next ITEM unless ( $category_to_process{$category} );
399
            }
418
            }
400
            if ($filter_itemtypes) {
419
            if ($filter_itemtypes) {
401
                my $it = uc Koha::Items->find( $row->{itemnumber} )->effective_itemtype();
420
                my $it = uc Koha::Items->find( $row->{itemnumber} )->effective_itemtype();
402
                next ITEM unless ( $itemtype_to_process{$it} );
421
                next ITEM unless ( $itemtype_to_process{$it} );
403
            }
422
            }
404
            printf ("Due %s: item %5s from borrower %5s to lost: %s\n", $row->{date_due}, $row->{itemnumber}, $row->{borrowernumber}, $lostvalue) if($verbose);
423
            printf(
405
            if($confirm) {
424
                "Due %s: item %5s from borrower %5s to lost: %s\n", $row->{date_due}, $row->{itemnumber},
406
                Koha::Items->find( $row->{itemnumber} )->itemlost($lostvalue)
425
                $row->{borrowernumber}, $lostvalue
407
                  ->store;
426
            ) if ($verbose);
427
            if ($confirm) {
428
                Koha::Items->find( $row->{itemnumber} )->itemlost($lostvalue)->store;
408
                if ( $charge && $charge eq $lostvalue ) {
429
                if ( $charge && $charge eq $lostvalue ) {
409
                    LostItem( $row->{'itemnumber'}, 'cronjob', $mark_returned );
430
                    LostItem( $row->{'itemnumber'}, 'cronjob', $mark_returned );
410
                } elsif ( $mark_returned ) {
431
                } elsif ($mark_returned) {
411
                    my $patron = Koha::Patrons->find( $row->{borrowernumber} );
432
                    my $patron = Koha::Patrons->find( $row->{borrowernumber} );
412
                    MarkIssueReturned($row->{borrowernumber},$row->{itemnumber},undef,$patron->privacy)
433
                    MarkIssueReturned( $row->{borrowernumber}, $row->{itemnumber}, undef, $patron->privacy );
413
                }
434
                }
414
            }
435
            }
415
            $count++;
436
            $count++;
416
        }
437
        }
417
        push @report, {
438
        push @report, {
418
           startrange => $startrange,
439
            startrange => $startrange,
419
             endrange => $endrange,
440
            endrange   => $endrange,
420
                range => "$startrange - $endrange",
441
            range      => "$startrange - $endrange",
421
                date1 => $date1,
442
            date1      => $date1,
422
                date2 => $date2,
443
            date2      => $date2,
423
            lostvalue => $lostvalue,
444
            lostvalue  => $lostvalue,
424
                count => $count,
445
            count      => $count,
425
        };
446
        };
426
        $total += $count;
447
        $total += $count;
427
    }
448
    }
Lines 429-449 foreach my $startrange (sort keys %$lost) { Link Here
429
}
450
}
430
451
431
sub summarize {
452
sub summarize {
432
    my $arg = shift;    # ref to array
453
    my $arg       = shift;             # ref to array
433
    my $got_items = shift || 0;     # print "count" line for items
454
    my $got_items = shift || 0;        # print "count" line for items
434
    my @report = @$arg or return;
455
    my @report    = @$arg or return;
435
    my $i = 0;
456
    my $i         = 0;
436
    for my $range (@report) {
457
    for my $range (@report) {
437
        printf "\nRange %s\nDue %3s - %3s days ago (%s to %s), lost => %s\n", ++$i,
458
        printf "\nRange %s\nDue %3s - %3s days ago (%s to %s), lost => %s\n", ++$i,
438
            map {$range->{$_}} qw(startrange endrange date2 date1 lostvalue);
459
            map { $range->{$_} } qw(startrange endrange date2 date1 lostvalue);
439
        $got_items and printf "  %4s items\n", $range->{count};
460
        $got_items and printf "  %4s items\n", $range->{count};
440
    }
461
    }
441
}
462
}
442
463
443
if (!$quiet){
464
if ( !$quiet ) {
444
    print "\n### LONGOVERDUE SUMMARY ###";
465
    print "\n### LONGOVERDUE SUMMARY ###";
445
    summarize (\@report, 1);
466
    summarize( \@report, 1 );
446
    print "\nTOTAL: $total items\n";
467
    print "\nTOTAL: $total items\n";
447
}
468
}
448
469
449
cronlogaction({ action => 'End', info => "COMPLETED" });
470
cronlogaction( { action => 'End', info => "COMPLETED" } );
450
- 

Return to bug 35169