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

(-)a/C4/Circulation.pm (+19 lines)
Lines 3930-3935 sub LostItem{ Link Here
3930
    while (my $transfer = $transfers->next) {
3930
    while (my $transfer = $transfers->next) {
3931
        $transfer->cancel({ reason => 'ItemLost', force => 1 });
3931
        $transfer->cancel({ reason => 'ItemLost', force => 1 });
3932
    }
3932
    }
3933
3934
    # RevertLostBibLevelHolds
3935
    my $hold = Koha::Holds->find({ itemnumber => $itemnumber, found => { '!=' =>  undef } });
3936
    if ( C4::Context->preference('RevertLostBibLevelHolds') && defined $hold ){
3937
        # syspref is enabled and there is a waiting hold for this item
3938
        if ( $hold->item_level_hold && defined $params->{cancel_reserve} ){
3939
            if ( $params->{cancel_reserve} ){
3940
                # cancel item-level hold
3941
                $hold->cancel;
3942
            } else { # eq 'revert'
3943
                # revert item-level hold's waiting status
3944
                $hold = C4::Reserves::RevertWaitingStatus({ itemnumber => $itemnumber });
3945
            }
3946
        }
3947
        if ( !$hold->item_level_hold ){
3948
            # revert biblio-level hold's waiting status
3949
            $hold = C4::Reserves::RevertWaitingStatus({ itemnumber => $itemnumber });
3950
        }
3951
    }
3933
}
3952
}
3934
3953
3935
sub GetOfflineOperations {
3954
sub GetOfflineOperations {
(-)a/catalogue/moredetail.pl (+3 lines)
Lines 235-240 foreach my $item (@items){ Link Here
235
                $item_info->{'nomod'}=1;
235
                $item_info->{'nomod'}=1;
236
        }
236
        }
237
    }
237
    }
238
239
    $item_info->{waiting_holds} = $item->{object}->holds({ found => 'W', item_level_hold => { '!=' => 0 } })->count;
240
238
    push @item_data, $item_info;
241
    push @item_data, $item_info;
239
}
242
}
240
243
(-)a/catalogue/updateitem.pl (+12 lines)
Lines 39-44 my $itemnotes_nonpublic=$cgi->param('itemnotes_nonpublic'); Link Here
39
my $withdrawn=$cgi->param('withdrawn');
39
my $withdrawn=$cgi->param('withdrawn');
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 $cancelhold=$cgi->param('cancelhold');
43
my $reverthold=$cgi->param('reverthold');
44
my $redirecturl=$cgi->param('redirecturl');
45
if ( defined $cancelhold ){
46
    LostItem($itemnumber, 'moredetail', 0, { cancel_reserve => $cancelhold });
47
} elsif ( defined $reverthold ){
48
    $cancelhold = 0;
49
    LostItem($itemnumber, 'moredetail', 0, { cancel_reserve => $cancelhold });
50
}
51
if ( defined $redirecturl ){
52
    print $cgi->redirect( $redirecturl . $biblionumber );
53
}
42
54
43
my $confirm=$cgi->param('confirm');
55
my $confirm=$cgi->param('confirm');
44
my $dbh = C4::Context->dbh;
56
my $dbh = C4::Context->dbh;
(-)a/cataloguing/additem.pl (+13 lines)
Lines 651-656 if( my $default_location = C4::Context->preference('NewItemsDefaultLocation') ) Link Here
651
}
651
}
652
652
653
my @ig = Koha::Biblio::ItemGroups->search({ biblio_id => $biblionumber })->as_list();
653
my @ig = Koha::Biblio::ItemGroups->search({ biblio_id => $biblionumber })->as_list();
654
655
if ( C4::Context->preference('RevertLostBibLevelHolds') ){
656
    my @lostitems = Koha::Items->search({ biblionumber => $biblionumber, itemlost => { '!=' => 0 } });
657
    # find if any of these lost items have waiting holds
658
    my @waitinglostitems;
659
    foreach my $lostitem ( @lostitems ){
660
        if ( $lostitem->holds({ found => 'W', item_level_hold => 1 })->count > 0 ){
661
            push( @waitinglostitems, $lostitem );
662
        }
663
    }
664
    $template->param( waitinglostitems => \@waitinglostitems );
665
}
666
654
# what's the next op ? it's what we are not in : an add if we're editing, otherwise, and edit.
667
# what's the next op ? it's what we are not in : an add if we're editing, otherwise, and edit.
655
$template->param(
668
$template->param(
656
    biblio       => $biblio,
669
    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 609-614 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
609
('ReturnLog','1',NULL,'If ON, enables the circulation (returns) log','YesNo'),
609
('ReturnLog','1',NULL,'If ON, enables the circulation (returns) log','YesNo'),
610
('ReturnpathDefault','',NULL,'Use this email address as return path or bounce address for undeliverable emails','Free'),
610
('ReturnpathDefault','',NULL,'Use this email address as return path or bounce address for undeliverable emails','Free'),
611
('OPACComments','1','','If ON, enables patron reviews of bibliographic records in the OPAC','YesNo'),
611
('OPACComments','1','','If ON, enables patron reviews of bibliographic records in the OPAC','YesNo'),
612
('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'),
612
('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'),
613
('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'),
613
('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'),
614
('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'),
614
('RoutingListAddReserves','0','','If ON the patrons on routing lists are automatically added to holds on the issue.','YesNo'),
615
('RoutingListAddReserves','0','','If ON the patrons on routing lists are automatically added to holds on the issue.','YesNo'),
Lines 634-639 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
634
('SelfCheckReceiptPrompt','1','NULL','If ON, print receipt dialog pops up when self checkout is finished','YesNo'),
635
('SelfCheckReceiptPrompt','1','NULL','If ON, print receipt dialog pops up when self checkout is finished','YesNo'),
635
('SelfCheckTimeout','120','','Define the number of seconds before the Web-based Self Checkout times out a patron','Integer'),
636
('SelfCheckTimeout','120','','Define the number of seconds before the Web-based Self Checkout times out a patron','Integer'),
636
('SendAllEmailsTo','',NULL,'All emails will be redirected to this email if it is not empty','free'),
637
('SendAllEmailsTo','',NULL,'All emails will be redirected to this email if it is not empty','free'),
638
('SendLostHoldNotices', '0', NULL, 'Send a notice to a borrower if their reserved and waiting item is marked as lost', 'YesNo'),
637
('SeparateHoldings','0',NULL,'Separate current branch holdings from other holdings','YesNo'),
639
('SeparateHoldings','0',NULL,'Separate current branch holdings from other holdings','YesNo'),
638
('SeparateHoldingsBranch','homebranch','homebranch|holdingbranch','Branch used to separate holdings','Choice'),
640
('SeparateHoldingsBranch','homebranch','homebranch|holdingbranch','Branch used to separate holdings','Choice'),
639
('SessionRestrictionByIP','1','Check for change in remote IP address for session security. Disable only when remote IP address changes frequently.','','YesNo'),
641
('SessionRestrictionByIP','1','Check for change in remote IP address for session security. Disable only when remote IP address changes frequently.','','YesNo'),
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref (+6 lines)
Lines 662-667 Circulation: Link Here
662
                  no: "Don't enable"
662
                  no: "Don't enable"
663
            - the item groups feature to allow collecting groups of items on a record together.
663
            - the item groups feature to allow collecting groups of items on a record together.
664
        -
664
        -
665
            - pref: RevertLostBibLevelHolds
666
              choices:
667
                  yes: Revert
668
                  no: "Don't revert"
669
            - waiting status of a hold if the allocated item is marked as Lost.
670
        -
665
            - In the staff client, split the holds queue into separate tables by
671
            - In the staff client, split the holds queue into separate tables by
666
            - pref: HoldsSplitQueue
672
            - pref: HoldsSplitQueue
667
              choices:
673
              choices:
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt (+12 lines)
Lines 96-101 Link Here
96
        <div class="listgroup">
96
        <div class="listgroup">
97
        <h3 id="item[% ITEM_DAT.itemnumber | html %]">Barcode [% ITEM_DAT.barcode | html %]  [% IF not_for_loan %][% not_for_loan_description | html %] [% END %]</h3>
97
        <h3 id="item[% ITEM_DAT.itemnumber | html %]">Barcode [% ITEM_DAT.barcode | html %]  [% IF not_for_loan %][% not_for_loan_description | html %] [% END %]</h3>
98
98
99
        [% IF ITEM_DAT.itemlost and ITEM_DAT.waiting_holds > 0  %]
100
            <div class="dialog alert">
101
                This item has a waiting item-level hold on it, and has been marked as lost.<br>
102
                <form action="updateitem.pl" method="post" class="inline">
103
                    <input type="hidden" name="itemnumber" value="[% ITEM_DAT.itemnumber %]">
104
                    <input type="hidden" name="biblionumber" value="[% ITEM_DAT.biblionumber %]">
105
                    <button type="submit" name="reverthold" value="1" class="btn btn-default"><i class="fa fa-undo"></i> Revert waiting status</button>
106
                    <button type="submit" name="cancelhold" value="1" class="btn btn-default"><i class="fa fa-times"></i> Cancel hold</button>
107
                </form>
108
            </div>
109
        [% END %]
110
99
        <h4>Item information [% UNLESS ( ITEM_DAT.nomod ) %]
111
        <h4>Item information [% UNLESS ( ITEM_DAT.nomod ) %]
100
         [% IF ( CAN_user_editcatalogue_edit_items ) %]
112
         [% IF ( CAN_user_editcatalogue_edit_items ) %]
101
            <a href="/cgi-bin/koha/cataloguing/additem.pl?op=edititem&amp;biblionumber=[% ITEM_DAT.biblionumber | uri %]&amp;itemnumber=[% ITEM_DAT.itemnumber | uri %]"><i class="fa fa-pencil"></i> Edit item</a>
113
            <a href="/cgi-bin/koha/cataloguing/additem.pl?op=edititem&amp;biblionumber=[% ITEM_DAT.biblionumber | uri %]&amp;itemnumber=[% ITEM_DAT.itemnumber | uri %]"><i class="fa fa-pencil"></i> Edit item</a>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/additem.tt (+19 lines)
Lines 66-71 Link Here
66
[% IF ( linked_analytics ) %]<div class="dialog alert"><strong>Cannot delete</strong>: item has linked <a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% biblio.biblionumber | uri %]&amp;analyze=1">analytics.</a>.</div>[% END %]
66
[% IF ( linked_analytics ) %]<div class="dialog alert"><strong>Cannot delete</strong>: item has linked <a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% biblio.biblionumber | uri %]&amp;analyze=1">analytics.</a>.</div>[% END %]
67
[% 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 %]
67
[% 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 %]
68
68
69
[% IF waitinglostitems %]
70
    <div class="alert">
71
        The following item(s) have a waiting item-level hold and have been marked as lost.
72
        <ul>
73
        [% FOREACH i IN waitinglostitems %]
74
            <br>
75
            <li>Item (<i>[% i.barcode %]</i>)
76
            <form action="../catalogue/updateitem.pl" method="post" class="inline">
77
                <input type="hidden" name="itemnumber" value="[% i.itemnumber %]">
78
                <input type="hidden" name="biblionumber" value="[% i.biblionumber %]">
79
                <input type="hidden" name="redirecturl" value="../cataloguing/additem.pl?biblionumber=">
80
                <button type="submit" name="reverthold" value="1" class="btn btn-default"><i class="fa fa-undo"></i> Revert waiting status</button>
81
                <button type="submit" name="cancelhold" value="1" class="btn btn-default"><i class="fa fa-times"></i> Cancel hold</button></li>
82
            </form>
83
        [% END %]
84
        </ul>
85
    </div>
86
[% END %]
87
69
<div id="cataloguing_additem_itemlist">
88
<div id="cataloguing_additem_itemlist">
70
    [% IF items %]
89
    [% IF items %]
71
        [% SET date_fields = [ 'dateaccessioned', 'onloan', 'datelastseen', 'datelastborrowed', 'replacementpricedate' ] %]
90
        [% 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 1408-1413 subtest 'ModReserveAffect logging' => sub { Link Here
1408
    like( $log->info, qr{$expected}, 'Timestamp logged is the current one' );
1408
    like( $log->info, qr{$expected}, 'Timestamp logged is the current one' );
1409
};
1409
};
1410
1410
1411
subtest 'Waiting item is marked as lost' => sub {
1412
1413
    plan tests => 15;
1414
1415
    # Set up data
1416
    my $biblio = $builder->build_sample_biblio();
1417
    my $item = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
1418
    my $patron = $builder->build_object({ class => 'Koha::Patrons' });
1419
1420
    ## TEST 1: BIBLIO-LEVEL HOLD
1421
1422
    # place biblio-level hold
1423
    my $reserve_id = AddReserve({
1424
        branchcode => $item->homebranch,
1425
        borrowernumber => $patron->borrowernumber,
1426
        biblionumber => $biblio->biblionumber,
1427
    });
1428
    my $hold = Koha::Holds->find( $reserve_id );
1429
1430
    is( $hold->item_level_hold, 0, 'Biblio-level hold placed' );
1431
1432
    # set hold as waiting and assign item
1433
    $hold->set_waiting;
1434
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1435
1436
    # mark item in biblio as lost
1437
    $item->itemlost( 1 )->store;
1438
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1439
1440
    # do test
1441
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1442
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1443
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1444
1445
    # enable preference
1446
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1447
1448
    # try again
1449
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1450
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1451
    $hold = Koha::Holds->find( $reserve_id );
1452
    is( $hold->found, undef, 'Hold waiting status has been reverted because RevertLostBibLevelHolds is enabled' );
1453
1454
    # clean up
1455
    $hold->cancel;
1456
    $item->itemlost( 0 );
1457
1458
    ## TEST 2: ITEM-LEVEL HOLD, REVERT
1459
1460
    # place item-level hold
1461
    $reserve_id = AddReserve({
1462
        branchcode => $item->homebranch,
1463
        borrowernumber => $patron->borrowernumber,
1464
        biblionumber => $biblio->biblionumber,
1465
        itemnumber => $item->itemnumber
1466
    });
1467
    $hold = Koha::Holds->find( $reserve_id );
1468
1469
    is( $hold->item_level_hold, 1, 'Item-level hold placed' );
1470
1471
    # set hold as waiting and assign item
1472
    $hold->set_waiting;
1473
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1474
1475
    # mark item in biblio as lost
1476
    $item->itemlost( 1 );
1477
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1478
1479
    # do test
1480
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1481
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1482
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1483
1484
    # enable preference
1485
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1486
1487
    # try again
1488
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1489
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1490
    $hold = Koha::Holds->find( $reserve_id );
1491
    is( $hold->found, undef, 'Hold waiting status has been reverted because RevertLostBibLevelHolds is enabled, and we chose not to cancel' );
1492
1493
    # clean up
1494
    $hold->cancel;
1495
    $item->itemlost( 0 );
1496
1497
    # TEST 3: ITEM-LEVEL HOLD, CANCEL
1498
1499
    # place item-level hold
1500
    $reserve_id = AddReserve({
1501
        branchcode => $item->homebranch,
1502
        borrowernumber => $patron->borrowernumber,
1503
        biblionumber => $biblio->biblionumber,
1504
        itemnumber => $item->itemnumber
1505
    });
1506
    $hold = Koha::Holds->find( $reserve_id );
1507
1508
    is( $hold->item_level_hold, 1, 'Item-level hold placed' );
1509
1510
    # set hold as waiting and assign item
1511
    $hold->set_waiting;
1512
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1513
1514
    # mark item in biblio as lost
1515
    $item->itemlost( 1 );
1516
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1517
1518
    # do test
1519
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1520
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 1 });
1521
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1522
1523
    # enable preference
1524
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1525
1526
    # try again
1527
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1528
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 1 });
1529
    $hold = Koha::Holds->find( $reserve_id );
1530
    is( Koha::Holds->find( $reserve_id ), undef, 'Hold has been cancelled with RevertLostBibLevelHolds enabled' );
1531
};
1532
1411
sub count_hold_print_messages {
1533
sub count_hold_print_messages {
1412
    my $message_count = $dbh->selectall_arrayref(q{
1534
    my $message_count = $dbh->selectall_arrayref(q{
1413
        SELECT COUNT(*)
1535
        SELECT COUNT(*)
1414
- 

Return to bug 20844