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

(-)a/C4/Circulation.pm (+19 lines)
Lines 4104-4109 sub LostItem{ Link Here
4104
    while (my $transfer = $transfers->next) {
4104
    while (my $transfer = $transfers->next) {
4105
        $transfer->cancel({ reason => 'ItemLost', force => 1 });
4105
        $transfer->cancel({ reason => 'ItemLost', force => 1 });
4106
    }
4106
    }
4107
4108
    # RevertLostBibLevelHolds
4109
    my $hold = Koha::Holds->find({ itemnumber => $itemnumber, found => { '!=' =>  undef } });
4110
    if ( C4::Context->preference('RevertLostBibLevelHolds') && defined $hold ){
4111
        # syspref is enabled and there is a waiting hold for this item
4112
        if ( $hold->item_level_hold && defined $params->{cancel_reserve} ){
4113
            if ( $params->{cancel_reserve} ){
4114
                # cancel item-level hold
4115
                $hold->cancel;
4116
            } else { # eq 'revert'
4117
                # revert item-level hold's waiting status
4118
                $hold = C4::Reserves::RevertWaitingStatus({ itemnumber => $itemnumber });
4119
            }
4120
        }
4121
        if ( !$hold->item_level_hold ){
4122
            # revert biblio-level hold's waiting status
4123
            $hold = C4::Reserves::RevertWaitingStatus({ itemnumber => $itemnumber });
4124
        }
4125
    }
4107
}
4126
}
4108
4127
4109
sub GetOfflineOperations {
4128
sub GetOfflineOperations {
(-)a/catalogue/moredetail.pl (+1 lines)
Lines 248-253 foreach my $item (@items){ Link Here
248
    );
248
    );
249
249
250
    $item_info->{nomod} = !$patron->can_edit_items_from( $item->homebranch );
250
    $item_info->{nomod} = !$patron->can_edit_items_from( $item->homebranch );
251
    $item_info->{waiting_holds} = $item->{object}->holds({ found => 'W', item_level_hold => { '!=' => 0 } })->count;
251
252
252
    push @item_data, $item_info;
253
    push @item_data, $item_info;
253
}
254
}
(-)a/catalogue/updateitem.pl (+12 lines)
Lines 40-45 my $withdrawn=$cgi->param('withdrawn'); Link Here
40
my $damaged=$cgi->param('damaged');
40
my $damaged=$cgi->param('damaged');
41
my $exclude_from_local_holds_priority = $cgi->param('exclude_from_local_holds_priority');
41
my $exclude_from_local_holds_priority = $cgi->param('exclude_from_local_holds_priority');
42
my $bookable = $cgi->param('bookable');
42
my $bookable = $cgi->param('bookable');
43
my $cancelhold=$cgi->param('cancelhold');
44
my $reverthold=$cgi->param('reverthold');
45
my $redirecturl=$cgi->param('redirecturl');
46
if ( defined $cancelhold ){
47
    LostItem($itemnumber, 'moredetail', 0, { cancel_reserve => $cancelhold });
48
} elsif ( defined $reverthold ){
49
    $cancelhold = 0;
50
    LostItem($itemnumber, 'moredetail', 0, { cancel_reserve => $cancelhold });
51
}
52
if ( defined $redirecturl ){
53
    print $cgi->redirect( $redirecturl . $biblionumber );
54
}
43
55
44
my $confirm=$cgi->param('confirm');
56
my $confirm=$cgi->param('confirm');
45
my $dbh = C4::Context->dbh;
57
my $dbh = C4::Context->dbh;
(-)a/cataloguing/additem.pl (+13 lines)
Lines 758-763 if( my $default_location = C4::Context->preference('NewItemsDefaultLocation') ) Link Here
758
my @ig = Koha::Biblio::ItemGroups->search({ biblio_id => $biblionumber })->as_list();
758
my @ig = Koha::Biblio::ItemGroups->search({ biblio_id => $biblionumber })->as_list();
759
#sort by display order
759
#sort by display order
760
my @sorted_ig = sort { $a->display_order <=> $b->display_order } @ig;
760
my @sorted_ig = sort { $a->display_order <=> $b->display_order } @ig;
761
762
if ( C4::Context->preference('RevertLostBibLevelHolds') ){
763
    my @lostitems = Koha::Items->search({ biblionumber => $biblionumber, itemlost => { '!=' => 0 } });
764
    # find if any of these lost items have waiting holds
765
    my @waitinglostitems;
766
    foreach my $lostitem ( @lostitems ){
767
        if ( $lostitem->holds({ found => 'W', item_level_hold => 1 })->count > 0 ){
768
            push( @waitinglostitems, $lostitem );
769
        }
770
    }
771
    $template->param( waitinglostitems => \@waitinglostitems );
772
}
773
761
# what's the next op ? it's what we are not in : an add if we're editing, otherwise, and edit.
774
# what's the next op ? it's what we are not in : an add if we're editing, otherwise, and edit.
762
$template->param(
775
$template->param(
763
    biblio       => $biblio,
776
    biblio       => $biblio,
(-)a/installer/data/mysql/atomicupdate/bug_20844-add_RevertLostBibLevelHolds_syspref.perl (+6 lines)
Line 0 Link Here
1
$DBversion = 'XXX';
2
if( CheckVersion( $DBversion ) ) {
3
    $dbh->do(q{INSERT IGNORE INTO systempreferences (variable,value,options,explanation,type) VALUES ('RevertLostBibLevelHolds', '0', NULL, 'If an item is marked as lost after being allocated for a biblio-level hold, revert the hold from its waiting status', 'YesNo') });
4
5
    NewVersion( $DBversion, 20844, "Add RevertLostBibLevelHolds system preference");
6
}
(-)a/installer/data/mysql/mandatory/sysprefs.sql (+2 lines)
Lines 649-654 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
649
('ReturnLog','1',NULL,'If ON, enables the circulation (returns) log','YesNo'),
649
('ReturnLog','1',NULL,'If ON, enables the circulation (returns) log','YesNo'),
650
('ReturnpathDefault','',NULL,'Use this email address as return path or bounce address for undeliverable emails','Free'),
650
('ReturnpathDefault','',NULL,'Use this email address as return path or bounce address for undeliverable emails','Free'),
651
('OPACComments','1','','If ON, enables patron reviews of bibliographic records in the OPAC','YesNo'),
651
('OPACComments','1','','If ON, enables patron reviews of bibliographic records in the OPAC','YesNo'),
652
('RevertLostBibLevelHolds', '0', NULL, 'If an item is marked as lost after being allocated for a biblio-level hold, revert the hold from its waiting status', 'YesNo'),
652
('RisExportAdditionalFields',  '', NULL ,  'Define additional RIS tags to export from MARC records in YAML format as an associative array with either a marc tag/subfield combination as the value, or a list of tag/subfield combinations.',  'textarea'),
653
('RisExportAdditionalFields',  '', NULL ,  'Define additional RIS tags to export from MARC records in YAML format as an associative array with either a marc tag/subfield combination as the value, or a list of tag/subfield combinations.',  'textarea'),
653
('RoundFinesAtPayment','0', NULL,'If enabled any fines with fractions of a cent will be rounded to the nearest cent when payments are coll    ected. e.g. 1.004 will be paid off by a 1.00 payment','YesNo'),
654
('RoundFinesAtPayment','0', NULL,'If enabled any fines with fractions of a cent will be rounded to the nearest cent when payments are coll    ected. e.g. 1.004 will be paid off by a 1.00 payment','YesNo'),
654
('RoutingListAddReserves','0','','If ON the patrons on routing lists are automatically added to holds on the issue.','YesNo'),
655
('RoutingListAddReserves','0','','If ON the patrons on routing lists are automatically added to holds on the issue.','YesNo'),
Lines 673-678 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
673
('SelfCheckReceiptPrompt','1','NULL','If ON, print receipt dialog pops up when self checkout is finished','YesNo'),
674
('SelfCheckReceiptPrompt','1','NULL','If ON, print receipt dialog pops up when self checkout is finished','YesNo'),
674
('SelfCheckTimeout','120','','Define the number of seconds before the Web-based Self Checkout times out a patron','Integer'),
675
('SelfCheckTimeout','120','','Define the number of seconds before the Web-based Self Checkout times out a patron','Integer'),
675
('SendAllEmailsTo','',NULL,'All emails will be redirected to this email if it is not empty','free'),
676
('SendAllEmailsTo','',NULL,'All emails will be redirected to this email if it is not empty','free'),
677
('SendLostHoldNotices', '0', NULL, 'Send a notice to a borrower if their reserved and waiting item is marked as lost', 'YesNo'),
676
('SeparateHoldings','0',NULL,'Separate current branch holdings from other holdings','YesNo'),
678
('SeparateHoldings','0',NULL,'Separate current branch holdings from other holdings','YesNo'),
677
('SeparateHoldingsBranch','homebranch','homebranch|holdingbranch','Branch used to separate holdings','Choice'),
679
('SeparateHoldingsBranch','homebranch','homebranch|holdingbranch','Branch used to separate holdings','Choice'),
678
('SerialsSearchResultsLimit', NULL, NULL, 'Serials search results limit', 'integer'),
680
('SerialsSearchResultsLimit', NULL, NULL, 'Serials search results limit', 'integer'),
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref (+6 lines)
Lines 682-687 Circulation: Link Here
682
                  0: "Don't store"
682
                  0: "Don't store"
683
            - 'the last patron to return an item. This setting is independent of the <a href="/cgi-bin/koha/admin/preferences.pl?op=search&searchfield=opacreadinghistory">opacreadinghistory</a> and <a href="/cgi-bin/koha/admin/preferences.pl?op=search&searchfield=AnonymousPatron">AnonymousPatron</a> system preferences.'
683
            - 'the last patron to return an item. This setting is independent of the <a href="/cgi-bin/koha/admin/preferences.pl?op=search&searchfield=opacreadinghistory">opacreadinghistory</a> and <a href="/cgi-bin/koha/admin/preferences.pl?op=search&searchfield=AnonymousPatron">AnonymousPatron</a> system preferences.'
684
    Holds policy:
684
    Holds policy:
685
        -
686
            - pref: RevertLostBibLevelHolds
687
              choices:
688
                  1: Revert
689
                  0: "Don't revert"
690
            - waiting status of a hold if the allocated item is marked as Lost.
685
        -
691
        -
686
            - In the staff interface, split the holds queue into separate tables by
692
            - In the staff interface, split the holds queue into separate tables by
687
            - pref: HoldsSplitQueue
693
            - pref: HoldsSplitQueue
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt (+12 lines)
Lines 119-124 Link Here
119
                                    Barcode [% ITEM_DAT.barcode | html %]  [% IF not_for_loan %][% not_for_loan_description | html %] [% END %]
119
                                    Barcode [% ITEM_DAT.barcode | html %]  [% IF not_for_loan %][% not_for_loan_description | html %] [% END %]
120
                                </h3>
120
                                </h3>
121
121
122
                                [% IF ITEM_DAT.itemlost and ITEM_DAT.waiting_holds > 0  %]
123
                                    <div class="dialog alert">
124
                                        This item has a waiting item-level hold on it, and has been marked as lost.<br>
125
                                        <form action="updateitem.pl" method="post" class="inline">
126
                                            <input type="hidden" name="itemnumber" value="[% ITEM_DAT.itemnumber %]">
127
                                            <input type="hidden" name="biblionumber" value="[% ITEM_DAT.biblionumber %]">
128
                                            <button type="submit" name="reverthold" value="1" class="btn btn-default"><i class="fa fa-undo"></i> Revert waiting status</button>
129
                                            <button type="submit" name="cancelhold" value="1" class="btn btn-default"><i class="fa fa-times"></i> Cancel hold</button>
130
                                        </form>
131
                                    </div>
132
                                [% END %]
133
122
                                <h4>
134
                                <h4>
123
                                    Item information
135
                                    Item information
124
                                    [% UNLESS ( ITEM_DAT.nomod ) %]
136
                                    [% UNLESS ( ITEM_DAT.nomod ) %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/additem.tt (+19 lines)
Lines 86-91 Link Here
86
[% IF last_item_for_hold %]<div class="dialog alert"><strong>Cannot delete</strong>: Last item for bibliographic record with biblio-level hold on it.</div>[% END %]
86
[% IF last_item_for_hold %]<div class="dialog alert"><strong>Cannot delete</strong>: Last item for bibliographic record with biblio-level hold on it.</div>[% END %]
87
[% IF item_not_found %]<div class="dialog alert"><strong>Cannot delete</strong>: Item not found.</div>[% END %]
87
[% IF item_not_found %]<div class="dialog alert"><strong>Cannot delete</strong>: Item not found.</div>[% END %]
88
88
89
[% IF waitinglostitems %]
90
    <div class="alert">
91
        The following item(s) have a waiting item-level hold and have been marked as lost.
92
        <ul>
93
        [% FOREACH i IN waitinglostitems %]
94
            <br>
95
            <li>Item (<i>[% i.barcode %]</i>)
96
            <form action="../catalogue/updateitem.pl" method="post" class="inline">
97
                <input type="hidden" name="itemnumber" value="[% i.itemnumber %]">
98
                <input type="hidden" name="biblionumber" value="[% i.biblionumber %]">
99
                <input type="hidden" name="redirecturl" value="../cataloguing/additem.pl?biblionumber=">
100
                <button type="submit" name="reverthold" value="1" class="btn btn-default"><i class="fa fa-undo"></i> Revert waiting status</button>
101
                <button type="submit" name="cancelhold" value="1" class="btn btn-default"><i class="fa fa-times"></i> Cancel hold</button></li>
102
            </form>
103
        [% END %]
104
        </ul>
105
    </div>
106
[% END %]
107
89
<div id="cataloguing_additem_itemlist">
108
<div id="cataloguing_additem_itemlist">
90
    [% IF items %]
109
    [% IF items %]
91
        [% SET date_fields = [ 'dateaccessioned', 'onloan', 'datelastseen', 'datelastborrowed', 'replacementpricedate' ] %]
110
        [% SET date_fields = [ 'dateaccessioned', 'onloan', 'datelastseen', 'datelastborrowed', 'replacementpricedate' ] %]
(-)a/t/db_dependent/Reserves.t (-2 / +123 lines)
Lines 17-23 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 77;
20
use Test::More tests => 78;
21
use Test::MockModule;
21
use Test::MockModule;
22
use Test::Warn;
22
use Test::Warn;
23
23
Lines 1405-1410 subtest 'ModReserveAffect logging' => sub { Link Here
1405
    like( $log->info, qr{$expected}, 'Timestamp logged is the current one' );
1405
    like( $log->info, qr{$expected}, 'Timestamp logged is the current one' );
1406
};
1406
};
1407
1407
1408
subtest 'Waiting item is marked as lost' => sub {
1409
1410
    plan tests => 15;
1411
1412
    # Set up data
1413
    my $biblio = $builder->build_sample_biblio();
1414
    my $item = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
1415
    my $patron = $builder->build_object({ class => 'Koha::Patrons' });
1416
1417
    ## TEST 1: BIBLIO-LEVEL HOLD
1418
1419
    # place biblio-level hold
1420
    my $reserve_id = AddReserve({
1421
        branchcode => $item->homebranch,
1422
        borrowernumber => $patron->borrowernumber,
1423
        biblionumber => $biblio->biblionumber,
1424
    });
1425
    my $hold = Koha::Holds->find( $reserve_id );
1426
1427
    is( $hold->item_level_hold, 0, 'Biblio-level hold placed' );
1428
1429
    # set hold as waiting and assign item
1430
    $hold->set_waiting;
1431
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1432
1433
    # mark item in biblio as lost
1434
    $item->itemlost( 1 )->store;
1435
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1436
1437
    # do test
1438
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1439
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1440
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1441
1442
    # enable preference
1443
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1444
1445
    # try again
1446
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1447
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1448
    $hold = Koha::Holds->find( $reserve_id );
1449
    is( $hold->found, undef, 'Hold waiting status has been reverted because RevertLostBibLevelHolds is enabled' );
1450
1451
    # clean up
1452
    $hold->cancel;
1453
    $item->itemlost( 0 );
1454
1455
    ## TEST 2: ITEM-LEVEL HOLD, REVERT
1456
1457
    # place item-level hold
1458
    $reserve_id = AddReserve({
1459
        branchcode => $item->homebranch,
1460
        borrowernumber => $patron->borrowernumber,
1461
        biblionumber => $biblio->biblionumber,
1462
        itemnumber => $item->itemnumber
1463
    });
1464
    $hold = Koha::Holds->find( $reserve_id );
1465
1466
    is( $hold->item_level_hold, 1, 'Item-level hold placed' );
1467
1468
    # set hold as waiting and assign item
1469
    $hold->set_waiting;
1470
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1471
1472
    # mark item in biblio as lost
1473
    $item->itemlost( 1 );
1474
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1475
1476
    # do test
1477
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1478
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1479
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1480
1481
    # enable preference
1482
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1483
1484
    # try again
1485
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1486
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1487
    $hold = Koha::Holds->find( $reserve_id );
1488
    is( $hold->found, undef, 'Hold waiting status has been reverted because RevertLostBibLevelHolds is enabled, and we chose not to cancel' );
1489
1490
    # clean up
1491
    $hold->cancel;
1492
    $item->itemlost( 0 );
1493
1494
    # TEST 3: ITEM-LEVEL HOLD, CANCEL
1495
1496
    # place item-level hold
1497
    $reserve_id = AddReserve({
1498
        branchcode => $item->homebranch,
1499
        borrowernumber => $patron->borrowernumber,
1500
        biblionumber => $biblio->biblionumber,
1501
        itemnumber => $item->itemnumber
1502
    });
1503
    $hold = Koha::Holds->find( $reserve_id );
1504
1505
    is( $hold->item_level_hold, 1, 'Item-level hold placed' );
1506
1507
    # set hold as waiting and assign item
1508
    $hold->set_waiting;
1509
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1510
1511
    # mark item in biblio as lost
1512
    $item->itemlost( 1 );
1513
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1514
1515
    # do test
1516
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1517
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 1 });
1518
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1519
1520
    # enable preference
1521
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1522
1523
    # try again
1524
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1525
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 1 });
1526
    $hold = Koha::Holds->find( $reserve_id );
1527
    is( Koha::Holds->find( $reserve_id ), undef, 'Hold has been cancelled with RevertLostBibLevelHolds enabled' );
1528
};
1529
1408
sub count_hold_print_messages {
1530
sub count_hold_print_messages {
1409
    my $message_count = $dbh->selectall_arrayref(q{
1531
    my $message_count = $dbh->selectall_arrayref(q{
1410
        SELECT COUNT(*)
1532
        SELECT COUNT(*)
1411
- 

Return to bug 20844