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

(-)a/C4/Circulation.pm (+19 lines)
Lines 4005-4010 sub LostItem{ Link Here
4005
    while (my $transfer = $transfers->next) {
4005
    while (my $transfer = $transfers->next) {
4006
        $transfer->cancel({ reason => 'ItemLost', force => 1 });
4006
        $transfer->cancel({ reason => 'ItemLost', force => 1 });
4007
    }
4007
    }
4008
4009
    # RevertLostBibLevelHolds
4010
    my $hold = Koha::Holds->find({ itemnumber => $itemnumber, found => { '!=' =>  undef } });
4011
    if ( C4::Context->preference('RevertLostBibLevelHolds') && defined $hold ){
4012
        # syspref is enabled and there is a waiting hold for this item
4013
        if ( $hold->item_level_hold && defined $params->{cancel_reserve} ){
4014
            if ( $params->{cancel_reserve} ){
4015
                # cancel item-level hold
4016
                $hold->cancel;
4017
            } else { # eq 'revert'
4018
                # revert item-level hold's waiting status
4019
                $hold = C4::Reserves::RevertWaitingStatus({ itemnumber => $itemnumber });
4020
            }
4021
        }
4022
        if ( !$hold->item_level_hold ){
4023
            # revert biblio-level hold's waiting status
4024
            $hold = C4::Reserves::RevertWaitingStatus({ itemnumber => $itemnumber });
4025
        }
4026
    }
4008
}
4027
}
4009
4028
4010
sub GetOfflineOperations {
4029
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 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 748-753 if( my $default_location = C4::Context->preference('NewItemsDefaultLocation') ) Link Here
748
}
748
}
749
749
750
my @ig = Koha::Biblio::ItemGroups->search({ biblio_id => $biblionumber })->as_list();
750
my @ig = Koha::Biblio::ItemGroups->search({ biblio_id => $biblionumber })->as_list();
751
752
if ( C4::Context->preference('RevertLostBibLevelHolds') ){
753
    my @lostitems = Koha::Items->search({ biblionumber => $biblionumber, itemlost => { '!=' => 0 } });
754
    # find if any of these lost items have waiting holds
755
    my @waitinglostitems;
756
    foreach my $lostitem ( @lostitems ){
757
        if ( $lostitem->holds({ found => 'W', item_level_hold => 1 })->count > 0 ){
758
            push( @waitinglostitems, $lostitem );
759
        }
760
    }
761
    $template->param( waitinglostitems => \@waitinglostitems );
762
}
763
751
# what's the next op ? it's what we are not in : an add if we're editing, otherwise, and edit.
764
# what's the next op ? it's what we are not in : an add if we're editing, otherwise, and edit.
752
$template->param(
765
$template->param(
753
    biblio       => $biblio,
766
    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 632-637 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
632
('ReturnLog','1',NULL,'If ON, enables the circulation (returns) log','YesNo'),
632
('ReturnLog','1',NULL,'If ON, enables the circulation (returns) log','YesNo'),
633
('ReturnpathDefault','',NULL,'Use this email address as return path or bounce address for undeliverable emails','Free'),
633
('ReturnpathDefault','',NULL,'Use this email address as return path or bounce address for undeliverable emails','Free'),
634
('OPACComments','1','','If ON, enables patron reviews of bibliographic records in the OPAC','YesNo'),
634
('OPACComments','1','','If ON, enables patron reviews of bibliographic records in the OPAC','YesNo'),
635
('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'),
635
('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'),
636
('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'),
636
('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'),
637
('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'),
637
('RoutingListAddReserves','0','','If ON the patrons on routing lists are automatically added to holds on the issue.','YesNo'),
638
('RoutingListAddReserves','0','','If ON the patrons on routing lists are automatically added to holds on the issue.','YesNo'),
Lines 658-663 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
658
('SelfCheckReceiptPrompt','1','NULL','If ON, print receipt dialog pops up when self checkout is finished','YesNo'),
659
('SelfCheckReceiptPrompt','1','NULL','If ON, print receipt dialog pops up when self checkout is finished','YesNo'),
659
('SelfCheckTimeout','120','','Define the number of seconds before the Web-based Self Checkout times out a patron','Integer'),
660
('SelfCheckTimeout','120','','Define the number of seconds before the Web-based Self Checkout times out a patron','Integer'),
660
('SendAllEmailsTo','',NULL,'All emails will be redirected to this email if it is not empty','free'),
661
('SendAllEmailsTo','',NULL,'All emails will be redirected to this email if it is not empty','free'),
662
('SendLostHoldNotices', '0', NULL, 'Send a notice to a borrower if their reserved and waiting item is marked as lost', 'YesNo'),
661
('SeparateHoldings','0',NULL,'Separate current branch holdings from other holdings','YesNo'),
663
('SeparateHoldings','0',NULL,'Separate current branch holdings from other holdings','YesNo'),
662
('SeparateHoldingsBranch','homebranch','homebranch|holdingbranch','Branch used to separate holdings','Choice'),
664
('SeparateHoldingsBranch','homebranch','homebranch|holdingbranch','Branch used to separate holdings','Choice'),
663
('SessionRestrictionByIP','1','Check for change in remote IP address for session security. Disable only when remote IP address changes frequently.','','YesNo'),
665
('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 684-689 Circulation: Link Here
684
                  1: Enable
684
                  1: Enable
685
                  0: "Don't enable"
685
                  0: "Don't enable"
686
            - the item groups feature to allow collecting groups of items on a record together.
686
            - the item groups feature to allow collecting groups of items on a record together.
687
        -
688
            - pref: RevertLostBibLevelHolds
689
              choices:
690
                  yes: Revert
691
                  no: "Don't revert"
692
            - waiting status of a hold if the allocated item is marked as Lost.
687
        -
693
        -
688
            - In the staff interface, split the holds queue into separate tables by
694
            - In the staff interface, split the holds queue into separate tables by
689
            - pref: HoldsSplitQueue
695
            - pref: HoldsSplitQueue
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt (+12 lines)
Lines 112-117 Link Here
112
                                    Barcode [% ITEM_DAT.barcode | html %]  [% IF not_for_loan %][% not_for_loan_description | html %] [% END %]
112
                                    Barcode [% ITEM_DAT.barcode | html %]  [% IF not_for_loan %][% not_for_loan_description | html %] [% END %]
113
                                </h3>
113
                                </h3>
114
114
115
                                [% IF ITEM_DAT.itemlost and ITEM_DAT.waiting_holds > 0  %]
116
                                    <div class="dialog alert">
117
                                        This item has a waiting item-level hold on it, and has been marked as lost.<br>
118
                                        <form action="updateitem.pl" method="post" class="inline">
119
                                            <input type="hidden" name="itemnumber" value="[% ITEM_DAT.itemnumber %]">
120
                                            <input type="hidden" name="biblionumber" value="[% ITEM_DAT.biblionumber %]">
121
                                            <button type="submit" name="reverthold" value="1" class="btn btn-default"><i class="fa fa-undo"></i> Revert waiting status</button>
122
                                            <button type="submit" name="cancelhold" value="1" class="btn btn-default"><i class="fa fa-times"></i> Cancel hold</button>
123
                                        </form>
124
                                    </div>
125
                                [% END %]
126
115
                                <h4>
127
                                <h4>
116
                                    Item information
128
                                    Item information
117
                                    [% UNLESS ( ITEM_DAT.nomod ) %]
129
                                    [% UNLESS ( ITEM_DAT.nomod ) %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/additem.tt (+19 lines)
Lines 76-81 Link Here
76
[% 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 %]
76
[% 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 %]
77
[% IF item_not_found %]<div class="dialog alert"><strong>Cannot delete</strong>: Item not found.</div>[% END %]
77
[% IF item_not_found %]<div class="dialog alert"><strong>Cannot delete</strong>: Item not found.</div>[% END %]
78
78
79
[% IF waitinglostitems %]
80
    <div class="alert">
81
        The following item(s) have a waiting item-level hold and have been marked as lost.
82
        <ul>
83
        [% FOREACH i IN waitinglostitems %]
84
            <br>
85
            <li>Item (<i>[% i.barcode %]</i>)
86
            <form action="../catalogue/updateitem.pl" method="post" class="inline">
87
                <input type="hidden" name="itemnumber" value="[% i.itemnumber %]">
88
                <input type="hidden" name="biblionumber" value="[% i.biblionumber %]">
89
                <input type="hidden" name="redirecturl" value="../cataloguing/additem.pl?biblionumber=">
90
                <button type="submit" name="reverthold" value="1" class="btn btn-default"><i class="fa fa-undo"></i> Revert waiting status</button>
91
                <button type="submit" name="cancelhold" value="1" class="btn btn-default"><i class="fa fa-times"></i> Cancel hold</button></li>
92
            </form>
93
        [% END %]
94
        </ul>
95
    </div>
96
[% END %]
97
79
<div id="cataloguing_additem_itemlist">
98
<div id="cataloguing_additem_itemlist">
80
    [% IF items %]
99
    [% IF items %]
81
        [% SET date_fields = [ 'dateaccessioned', 'onloan', 'datelastseen', 'datelastborrowed', 'replacementpricedate' ] %]
100
        [% 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 1422-1427 subtest 'ModReserveAffect logging' => sub { Link Here
1422
    like( $log->info, qr{$expected}, 'Timestamp logged is the current one' );
1422
    like( $log->info, qr{$expected}, 'Timestamp logged is the current one' );
1423
};
1423
};
1424
1424
1425
subtest 'Waiting item is marked as lost' => sub {
1426
1427
    plan tests => 15;
1428
1429
    # Set up data
1430
    my $biblio = $builder->build_sample_biblio();
1431
    my $item = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
1432
    my $patron = $builder->build_object({ class => 'Koha::Patrons' });
1433
1434
    ## TEST 1: BIBLIO-LEVEL HOLD
1435
1436
    # place biblio-level hold
1437
    my $reserve_id = AddReserve({
1438
        branchcode => $item->homebranch,
1439
        borrowernumber => $patron->borrowernumber,
1440
        biblionumber => $biblio->biblionumber,
1441
    });
1442
    my $hold = Koha::Holds->find( $reserve_id );
1443
1444
    is( $hold->item_level_hold, 0, 'Biblio-level hold placed' );
1445
1446
    # set hold as waiting and assign item
1447
    $hold->set_waiting;
1448
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1449
1450
    # mark item in biblio as lost
1451
    $item->itemlost( 1 )->store;
1452
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1453
1454
    # do test
1455
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1456
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1457
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1458
1459
    # enable preference
1460
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1461
1462
    # try again
1463
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1464
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1465
    $hold = Koha::Holds->find( $reserve_id );
1466
    is( $hold->found, undef, 'Hold waiting status has been reverted because RevertLostBibLevelHolds is enabled' );
1467
1468
    # clean up
1469
    $hold->cancel;
1470
    $item->itemlost( 0 );
1471
1472
    ## TEST 2: ITEM-LEVEL HOLD, REVERT
1473
1474
    # place item-level hold
1475
    $reserve_id = AddReserve({
1476
        branchcode => $item->homebranch,
1477
        borrowernumber => $patron->borrowernumber,
1478
        biblionumber => $biblio->biblionumber,
1479
        itemnumber => $item->itemnumber
1480
    });
1481
    $hold = Koha::Holds->find( $reserve_id );
1482
1483
    is( $hold->item_level_hold, 1, 'Item-level hold placed' );
1484
1485
    # set hold as waiting and assign item
1486
    $hold->set_waiting;
1487
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1488
1489
    # mark item in biblio as lost
1490
    $item->itemlost( 1 );
1491
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1492
1493
    # do test
1494
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1495
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1496
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1497
1498
    # enable preference
1499
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1500
1501
    # try again
1502
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1503
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1504
    $hold = Koha::Holds->find( $reserve_id );
1505
    is( $hold->found, undef, 'Hold waiting status has been reverted because RevertLostBibLevelHolds is enabled, and we chose not to cancel' );
1506
1507
    # clean up
1508
    $hold->cancel;
1509
    $item->itemlost( 0 );
1510
1511
    # TEST 3: ITEM-LEVEL HOLD, CANCEL
1512
1513
    # place item-level hold
1514
    $reserve_id = AddReserve({
1515
        branchcode => $item->homebranch,
1516
        borrowernumber => $patron->borrowernumber,
1517
        biblionumber => $biblio->biblionumber,
1518
        itemnumber => $item->itemnumber
1519
    });
1520
    $hold = Koha::Holds->find( $reserve_id );
1521
1522
    is( $hold->item_level_hold, 1, 'Item-level hold placed' );
1523
1524
    # set hold as waiting and assign item
1525
    $hold->set_waiting;
1526
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1527
1528
    # mark item in biblio as lost
1529
    $item->itemlost( 1 );
1530
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1531
1532
    # do test
1533
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1534
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 1 });
1535
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1536
1537
    # enable preference
1538
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1539
1540
    # try again
1541
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1542
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 1 });
1543
    $hold = Koha::Holds->find( $reserve_id );
1544
    is( Koha::Holds->find( $reserve_id ), undef, 'Hold has been cancelled with RevertLostBibLevelHolds enabled' );
1545
};
1546
1425
sub count_hold_print_messages {
1547
sub count_hold_print_messages {
1426
    my $message_count = $dbh->selectall_arrayref(q{
1548
    my $message_count = $dbh->selectall_arrayref(q{
1427
        SELECT COUNT(*)
1549
        SELECT COUNT(*)
1428
- 

Return to bug 20844