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

(-)a/C4/Circulation.pm (+19 lines)
Lines 3884-3889 sub LostItem{ Link Here
3884
    while (my $transfer = $transfers->next) {
3884
    while (my $transfer = $transfers->next) {
3885
        $transfer->cancel({ reason => 'ItemLost', force => 1 });
3885
        $transfer->cancel({ reason => 'ItemLost', force => 1 });
3886
    }
3886
    }
3887
3888
    # RevertLostBibLevelHolds
3889
    my $hold = Koha::Holds->find({ itemnumber => $itemnumber, found => { '!=' =>  undef } });
3890
    if ( C4::Context->preference('RevertLostBibLevelHolds') && defined $hold ){
3891
        # syspref is enabled and there is a waiting hold for this item
3892
        if ( $hold->item_level_hold && defined $params->{cancel_reserve} ){
3893
            if ( $params->{cancel_reserve} ){
3894
                # cancel item-level hold
3895
                $hold->cancel;
3896
            } else { # eq 'revert'
3897
                # revert item-level hold's waiting status
3898
                $hold = C4::Reserves::RevertWaitingStatus({ itemnumber => $itemnumber });
3899
            }
3900
        }
3901
        if ( !$hold->item_level_hold ){
3902
            # revert biblio-level hold's waiting status
3903
            $hold = C4::Reserves::RevertWaitingStatus({ itemnumber => $itemnumber });
3904
        }
3905
    }
3887
}
3906
}
3888
3907
3889
sub GetOfflineOperations {
3908
sub GetOfflineOperations {
(-)a/catalogue/moredetail.pl (+2 lines)
Lines 242-247 foreach my $item (@items){ Link Here
242
        my $curr_borrower = Koha::Patrons->find( $item->{borrowernumber} );
242
        my $curr_borrower = Koha::Patrons->find( $item->{borrowernumber} );
243
        $item->{patron} = $curr_borrower;
243
        $item->{patron} = $curr_borrower;
244
    }
244
    }
245
246
    $item->{waiting_holds} = $item->{object}->holds({ found => 'W', item_level_hold => { '!=' => 0 } })->count;
245
}
247
}
246
248
247
my $mss = Koha::MarcSubfieldStructures->search({ frameworkcode => $fw, kohafield => 'items.itemlost', authorised_value => [ -and => {'!=' => undef }, {'!=' => ''}] });
249
my $mss = Koha::MarcSubfieldStructures->search({ frameworkcode => $fw, kohafield => 'items.itemlost', authorised_value => [ -and => {'!=' => undef }, {'!=' => ''}] });
(-)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 (+12 lines)
Lines 996-1001 foreach my $tag ( keys %{$tagslib}){ Link Here
996
}
996
}
997
@loop_data = sort { $a->{display_order} <=> $b->{display_order} || $a->{subfield} cmp $b->{subfield} } @loop_data;
997
@loop_data = sort { $a->{display_order} <=> $b->{display_order} || $a->{subfield} cmp $b->{subfield} } @loop_data;
998
998
999
if ( C4::Context->preference('RevertLostBibLevelHolds') ){
1000
    my @lostitems = Koha::Items->search({ biblionumber => $biblionumber, itemlost => { '!=' => 0 } });
1001
    # find if any of these lost items have waiting holds
1002
    my @waitinglostitems;
1003
    foreach my $lostitem ( @lostitems ){
1004
        if ( $lostitem->holds({ found => 'W', item_level_hold => 1 })->count > 0 ){
1005
            push( @waitinglostitems, $lostitem );
1006
        }
1007
    }
1008
    $template->param( waitinglostitems => \@waitinglostitems );
1009
}
1010
999
# what's the next op ? it's what we are not in : an add if we're editing, otherwise, and edit.
1011
# what's the next op ? it's what we are not in : an add if we're editing, otherwise, and edit.
1000
$template->param(
1012
$template->param(
1001
    biblionumber => $biblionumber,
1013
    biblionumber => $biblionumber,
(-)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 581-586 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
581
('ReturnLog','1',NULL,'If ON, enables the circulation (returns) log','YesNo'),
581
('ReturnLog','1',NULL,'If ON, enables the circulation (returns) log','YesNo'),
582
('ReturnpathDefault','',NULL,'Use this email address as return path or bounce address for undeliverable emails','Free'),
582
('ReturnpathDefault','',NULL,'Use this email address as return path or bounce address for undeliverable emails','Free'),
583
('OPACComments','1','','If ON, enables patron reviews of bibliographic records in the OPAC','YesNo'),
583
('OPACComments','1','','If ON, enables patron reviews of bibliographic records in the OPAC','YesNo'),
584
('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'),
584
('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'),
585
('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'),
585
('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'),
586
('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'),
586
('RoutingListAddReserves','0','','If ON the patrons on routing lists are automatically added to holds on the issue.','YesNo'),
587
('RoutingListAddReserves','0','','If ON the patrons on routing lists are automatically added to holds on the issue.','YesNo'),
Lines 605-610 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
605
('SelfCheckReceiptPrompt','1','NULL','If ON, print receipt dialog pops up when self checkout is finished','YesNo'),
606
('SelfCheckReceiptPrompt','1','NULL','If ON, print receipt dialog pops up when self checkout is finished','YesNo'),
606
('SelfCheckTimeout','120','','Define the number of seconds before the Web-based Self Checkout times out a patron','Integer'),
607
('SelfCheckTimeout','120','','Define the number of seconds before the Web-based Self Checkout times out a patron','Integer'),
607
('SendAllEmailsTo','',NULL,'All emails will be redirected to this email if it is not empty','free'),
608
('SendAllEmailsTo','',NULL,'All emails will be redirected to this email if it is not empty','free'),
609
('SendLostHoldNotices', '0', NULL, 'Send a notice to a borrower if their reserved and waiting item is marked as lost', 'YesNo'),
608
('SeparateHoldings','0',NULL,'Separate current branch holdings from other holdings','YesNo'),
610
('SeparateHoldings','0',NULL,'Separate current branch holdings from other holdings','YesNo'),
609
('SeparateHoldingsBranch','homebranch','homebranch|holdingbranch','Branch used to separate holdings','Choice'),
611
('SeparateHoldingsBranch','homebranch','homebranch|holdingbranch','Branch used to separate holdings','Choice'),
610
('SessionRestrictionByIP','1','Check for change in remote IP address for session security. Disable only when remote IP address changes frequently.','','YesNo'),
612
('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 660-665 Circulation: Link Here
660
            - '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.'
660
            - '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.'
661
    Holds policy:
661
    Holds policy:
662
        -
662
        -
663
            - pref: RevertLostBibLevelHolds
664
              choices:
665
                  yes: Revert
666
                  no: "Don't revert"
667
            - waiting status of a hold if the allocated item is marked as Lost.
668
        -
663
            - In the staff interface, split the holds queue into separate tables by
669
            - In the staff interface, split the holds queue into separate tables by
664
            - pref: HoldsSplitQueue
670
            - pref: HoldsSplitQueue
665
              choices:
671
              choices:
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt (+12 lines)
Lines 89-94 Link Here
89
        <div class="listgroup">
89
        <div class="listgroup">
90
        <h3 id="item[% ITEM_DAT.itemnumber | html %]">Barcode [% ITEM_DAT.barcode | html %]  [% IF ( ITEM_DAT.notforloantext ) %][% ITEM_DAT.notforloantext | html %] [% END %]</h3>
90
        <h3 id="item[% ITEM_DAT.itemnumber | html %]">Barcode [% ITEM_DAT.barcode | html %]  [% IF ( ITEM_DAT.notforloantext ) %][% ITEM_DAT.notforloantext | html %] [% END %]</h3>
91
91
92
        [% IF ITEM_DAT.itemlost and ITEM_DAT.waiting_holds > 0  %]
93
            <div class="dialog alert">
94
                This item has a waiting item-level hold on it, and has been marked as lost.<br>
95
                <form action="updateitem.pl" method="post" class="inline">
96
                    <input type="hidden" name="itemnumber" value="[% ITEM_DAT.itemnumber %]">
97
                    <input type="hidden" name="biblionumber" value="[% ITEM_DAT.biblionumber %]">
98
                    <button type="submit" name="reverthold" value="1" class="btn btn-default"><i class="fa fa-undo"></i> Revert waiting status</button>
99
                    <button type="submit" name="cancelhold" value="1" class="btn btn-default"><i class="fa fa-times"></i> Cancel hold</button>
100
                </form>
101
            </div>
102
        [% END %]
103
92
        <h4>Item information [% UNLESS ( ITEM_DAT.nomod ) %]
104
        <h4>Item information [% UNLESS ( ITEM_DAT.nomod ) %]
93
         [% IF ( CAN_user_editcatalogue_edit_items ) %]
105
         [% IF ( CAN_user_editcatalogue_edit_items ) %]
94
            <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>
106
            <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 58-63 Link Here
58
[% IF ( linked_analytics ) %]<div class="dialog alert"><strong>Cannot delete</strong>: item has linked <a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% biblionumber | uri %]&amp;analyze=1">analytics.</a>.</div>[% END %]
58
[% IF ( linked_analytics ) %]<div class="dialog alert"><strong>Cannot delete</strong>: item has linked <a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% biblionumber | uri %]&amp;analyze=1">analytics.</a>.</div>[% END %]
59
[% 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 %]
59
[% 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 %]
60
60
61
[% IF waitinglostitems %]
62
    <div class="alert">
63
        The following item(s) have a waiting item-level hold and have been marked as lost.
64
        <ul>
65
        [% FOREACH i IN waitinglostitems %]
66
            <br>
67
            <li>Item (<i>[% i.barcode %]</i>)
68
            <form action="../catalogue/updateitem.pl" method="post" class="inline">
69
                <input type="hidden" name="itemnumber" value="[% i.itemnumber %]">
70
                <input type="hidden" name="biblionumber" value="[% i.biblionumber %]">
71
                <input type="hidden" name="redirecturl" value="../cataloguing/additem.pl?biblionumber=">
72
                <button type="submit" name="reverthold" value="1" class="btn btn-default"><i class="fa fa-undo"></i> Revert waiting status</button>
73
                <button type="submit" name="cancelhold" value="1" class="btn btn-default"><i class="fa fa-times"></i> Cancel hold</button></li>
74
            </form>
75
        [% END %]
76
        </ul>
77
    </div>
78
[% END %]
79
61
<div id="cataloguing_additem_itemlist">
80
<div id="cataloguing_additem_itemlist">
62
    [% IF ( item_loop ) %]
81
    [% IF ( item_loop ) %]
63
        [% SET date_fields = [ 'dateaccessioned', 'onloan', 'datelastseen', 'datelastborrowed', 'replacementpricedate' ] %]
82
        [% 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 => 68;
20
use Test::More tests => 69;
21
use Test::MockModule;
21
use Test::MockModule;
22
use Test::Warn;
22
use Test::Warn;
23
23
Lines 1332-1337 subtest 'ModReserveAffect logging' => sub { Link Here
1332
    like( $log->info, qr{$expected}, 'Timestamp logged is the current one' );
1332
    like( $log->info, qr{$expected}, 'Timestamp logged is the current one' );
1333
};
1333
};
1334
1334
1335
subtest 'Waiting item is marked as lost' => sub {
1336
1337
    plan tests => 15;
1338
1339
    # Set up data
1340
    my $biblio = $builder->build_sample_biblio();
1341
    my $item = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
1342
    my $patron = $builder->build_object({ class => 'Koha::Patrons' });
1343
1344
    ## TEST 1: BIBLIO-LEVEL HOLD
1345
1346
    # place biblio-level hold
1347
    my $reserve_id = AddReserve({
1348
        branchcode => $item->homebranch,
1349
        borrowernumber => $patron->borrowernumber,
1350
        biblionumber => $biblio->biblionumber,
1351
    });
1352
    my $hold = Koha::Holds->find( $reserve_id );
1353
1354
    is( $hold->item_level_hold, 0, 'Biblio-level hold placed' );
1355
1356
    # set hold as waiting and assign item
1357
    $hold->set_waiting;
1358
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1359
1360
    # mark item in biblio as lost
1361
    $item->itemlost( 1 )->store;
1362
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1363
1364
    # do test
1365
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1366
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1367
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1368
1369
    # enable preference
1370
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1371
1372
    # try again
1373
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1374
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1375
    $hold = Koha::Holds->find( $reserve_id );
1376
    is( $hold->found, undef, 'Hold waiting status has been reverted because RevertLostBibLevelHolds is enabled' );
1377
1378
    # clean up
1379
    $hold->cancel;
1380
    $item->itemlost( 0 );
1381
1382
    ## TEST 2: ITEM-LEVEL HOLD, REVERT
1383
1384
    # place item-level hold
1385
    $reserve_id = AddReserve({
1386
        branchcode => $item->homebranch,
1387
        borrowernumber => $patron->borrowernumber,
1388
        biblionumber => $biblio->biblionumber,
1389
        itemnumber => $item->itemnumber
1390
    });
1391
    $hold = Koha::Holds->find( $reserve_id );
1392
1393
    is( $hold->item_level_hold, 1, 'Item-level hold placed' );
1394
1395
    # set hold as waiting and assign item
1396
    $hold->set_waiting;
1397
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1398
1399
    # mark item in biblio as lost
1400
    $item->itemlost( 1 );
1401
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1402
1403
    # do test
1404
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1405
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1406
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1407
1408
    # enable preference
1409
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1410
1411
    # try again
1412
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1413
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1414
    $hold = Koha::Holds->find( $reserve_id );
1415
    is( $hold->found, undef, 'Hold waiting status has been reverted because RevertLostBibLevelHolds is enabled, and we chose not to cancel' );
1416
1417
    # clean up
1418
    $hold->cancel;
1419
    $item->itemlost( 0 );
1420
1421
    # TEST 3: ITEM-LEVEL HOLD, CANCEL
1422
1423
    # place item-level hold
1424
    $reserve_id = AddReserve({
1425
        branchcode => $item->homebranch,
1426
        borrowernumber => $patron->borrowernumber,
1427
        biblionumber => $biblio->biblionumber,
1428
        itemnumber => $item->itemnumber
1429
    });
1430
    $hold = Koha::Holds->find( $reserve_id );
1431
1432
    is( $hold->item_level_hold, 1, 'Item-level hold placed' );
1433
1434
    # set hold as waiting and assign item
1435
    $hold->set_waiting;
1436
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1437
1438
    # mark item in biblio as lost
1439
    $item->itemlost( 1 );
1440
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1441
1442
    # do test
1443
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1444
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 1 });
1445
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1446
1447
    # enable preference
1448
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1449
1450
    # try again
1451
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1452
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 1 });
1453
    $hold = Koha::Holds->find( $reserve_id );
1454
    is( Koha::Holds->find( $reserve_id ), undef, 'Hold has been cancelled with RevertLostBibLevelHolds enabled' );
1455
};
1456
1335
sub count_hold_print_messages {
1457
sub count_hold_print_messages {
1336
    my $message_count = $dbh->selectall_arrayref(q{
1458
    my $message_count = $dbh->selectall_arrayref(q{
1337
        SELECT COUNT(*)
1459
        SELECT COUNT(*)
1338
- 

Return to bug 20844