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

(-)a/C4/Circulation.pm (+19 lines)
Lines 3917-3922 sub LostItem{ Link Here
3917
        Koha::Items->find($itemnumber)->holdingbranch($frombranch)->store({ skip_record_index => $params->{skip_record_index} });
3917
        Koha::Items->find($itemnumber)->holdingbranch($frombranch)->store({ skip_record_index => $params->{skip_record_index} });
3918
    }
3918
    }
3919
    my $transferdeleted = DeleteTransfer($itemnumber);
3919
    my $transferdeleted = DeleteTransfer($itemnumber);
3920
3921
    # RevertLostBibLevelHolds
3922
    my $hold = Koha::Holds->find({ itemnumber => $itemnumber, found => { '!=' =>  undef } });
3923
    if ( C4::Context->preference('RevertLostBibLevelHolds') && defined $hold ){
3924
        # syspref is enabled and there is a waiting hold for this item
3925
        if ( $hold->item_level_hold && defined $params->{cancel_reserve} ){
3926
            if ( $params->{cancel_reserve} ){
3927
                # cancel item-level hold
3928
                $hold->cancel;
3929
            } else { # eq 'revert'
3930
                # revert item-level hold's waiting status
3931
                $hold = C4::Reserves::RevertWaitingStatus({ itemnumber => $itemnumber });
3932
            }
3933
        }
3934
        if ( !$hold->item_level_hold ){
3935
            # revert biblio-level hold's waiting status
3936
            $hold = C4::Reserves::RevertWaitingStatus({ itemnumber => $itemnumber });
3937
        }
3938
    }
3920
}
3939
}
3921
3940
3922
sub GetOfflineOperations {
3941
sub GetOfflineOperations {
(-)a/catalogue/moredetail.pl (+2 lines)
Lines 244-249 foreach my $item (@items){ Link Here
244
        my $curr_borrower = Koha::Patrons->find( $item->{borrowernumber} );
244
        my $curr_borrower = Koha::Patrons->find( $item->{borrowernumber} );
245
        $item->{patron} = $curr_borrower;
245
        $item->{patron} = $curr_borrower;
246
    }
246
    }
247
248
    $item->{waiting_holds} = $item->{object}->holds({ found => 'W', item_level_hold => { '!=' => 0 } })->count;
247
}
249
}
248
250
249
my $mss = Koha::MarcSubfieldStructures->search({ frameworkcode => $fw, kohafield => 'items.itemlost', authorised_value => [ -and => {'!=' => undef }, {'!=' => ''}] });
251
my $mss = Koha::MarcSubfieldStructures->search({ frameworkcode => $fw, kohafield => 'items.itemlost', authorised_value => [ -and => {'!=' => undef }, {'!=' => ''}] });
(-)a/catalogue/updateitem.pl (+12 lines)
Lines 41-46 my $itemnotes_nonpublic=$cgi->param('itemnotes_nonpublic'); Link Here
41
my $withdrawn=$cgi->param('withdrawn');
41
my $withdrawn=$cgi->param('withdrawn');
42
my $damaged=$cgi->param('damaged');
42
my $damaged=$cgi->param('damaged');
43
my $exclude_from_local_holds_priority = $cgi->param('exclude_from_local_holds_priority');
43
my $exclude_from_local_holds_priority = $cgi->param('exclude_from_local_holds_priority');
44
my $cancelhold=$cgi->param('cancelhold');
45
my $reverthold=$cgi->param('reverthold');
46
my $redirecturl=$cgi->param('redirecturl');
47
if ( defined $cancelhold ){
48
    LostItem($itemnumber, 'moredetail', 0, { cancel_reserve => $cancelhold });
49
} elsif ( defined $reverthold ){
50
    $cancelhold = 0;
51
    LostItem($itemnumber, 'moredetail', 0, { cancel_reserve => $cancelhold });
52
}
53
if ( defined $redirecturl ){
54
    print $cgi->redirect( $redirecturl . $biblionumber );
55
}
44
56
45
my $confirm=$cgi->param('confirm');
57
my $confirm=$cgi->param('confirm');
46
my $dbh = C4::Context->dbh;
58
my $dbh = C4::Context->dbh;
(-)a/cataloguing/additem.pl (+12 lines)
Lines 986-991 foreach my $tag ( keys %{$tagslib}){ Link Here
986
}
986
}
987
@loop_data = sort { $a->{display_order} <=> $b->{display_order} || $a->{subfield} cmp $b->{subfield} } @loop_data;
987
@loop_data = sort { $a->{display_order} <=> $b->{display_order} || $a->{subfield} cmp $b->{subfield} } @loop_data;
988
988
989
if ( C4::Context->preference('RevertLostBibLevelHolds') ){
990
    my @lostitems = Koha::Items->search({ biblionumber => $biblionumber, itemlost => { '!=' => 0 } });
991
    # find if any of these lost items have waiting holds
992
    my @waitinglostitems;
993
    foreach my $lostitem ( @lostitems ){
994
        if ( $lostitem->holds({ found => 'W', item_level_hold => 1 })->count > 0 ){
995
            push( @waitinglostitems, $lostitem );
996
        }
997
    }
998
    $template->param( waitinglostitems => \@waitinglostitems );
999
}
1000
989
# what's the next op ? it's what we are not in : an add if we're editing, otherwise, and edit.
1001
# what's the next op ? it's what we are not in : an add if we're editing, otherwise, and edit.
990
$template->param(
1002
$template->param(
991
    biblionumber => $biblionumber,
1003
    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 573-578 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
573
('ReturnLog','1',NULL,'If ON, enables the circulation (returns) log','YesNo'),
573
('ReturnLog','1',NULL,'If ON, enables the circulation (returns) log','YesNo'),
574
('ReturnpathDefault','',NULL,'Use this email address as return path or bounce address for undeliverable emails','Free'),
574
('ReturnpathDefault','',NULL,'Use this email address as return path or bounce address for undeliverable emails','Free'),
575
('OPACComments','1','','If ON, enables patron reviews of bibliographic records in the OPAC','YesNo'),
575
('OPACComments','1','','If ON, enables patron reviews of bibliographic records in the OPAC','YesNo'),
576
('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'),
576
('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'),
577
('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'),
577
('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'),
578
('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'),
578
('RoutingListAddReserves','0','','If ON the patrons on routing lists are automatically added to holds on the issue.','YesNo'),
579
('RoutingListAddReserves','0','','If ON the patrons on routing lists are automatically added to holds on the issue.','YesNo'),
Lines 596-601 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
596
('SelfCheckReceiptPrompt','1','NULL','If ON, print receipt dialog pops up when self checkout is finished','YesNo'),
597
('SelfCheckReceiptPrompt','1','NULL','If ON, print receipt dialog pops up when self checkout is finished','YesNo'),
597
('SelfCheckTimeout','120','','Define the number of seconds before the Web-based Self Checkout times out a patron','Integer'),
598
('SelfCheckTimeout','120','','Define the number of seconds before the Web-based Self Checkout times out a patron','Integer'),
598
('SendAllEmailsTo','',NULL,'All emails will be redirected to this email if it is not empty','free'),
599
('SendAllEmailsTo','',NULL,'All emails will be redirected to this email if it is not empty','free'),
600
('SendLostHoldNotices', '0', NULL, 'Send a notice to a borrower if their reserved and waiting item is marked as lost', 'YesNo'),
599
('SeparateHoldings','0',NULL,'Separate current branch holdings from other holdings','YesNo'),
601
('SeparateHoldings','0',NULL,'Separate current branch holdings from other holdings','YesNo'),
600
('SeparateHoldingsBranch','homebranch','homebranch|holdingbranch','Branch used to separate holdings','Choice'),
602
('SeparateHoldingsBranch','homebranch','homebranch|holdingbranch','Branch used to separate holdings','Choice'),
601
('SessionRestrictionByIP','1','Check for change in remote IP address for session security. Disable only when remote IP address changes frequently.','','YesNo'),
603
('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 659-664 Circulation: Link Here
659
            - '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.'
659
            - '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
    Holds policy:
660
    Holds policy:
661
        -
661
        -
662
            - pref: RevertLostBibLevelHolds
663
              choices:
664
                  yes: Revert
665
                  no: "Don't revert"
666
            - waiting status of a hold if the allocated item is marked as Lost.
667
        -
662
            - In the staff interface, split the holds queue into separate tables by
668
            - In the staff interface, split the holds queue into separate tables by
663
            - pref: HoldsSplitQueue
669
            - pref: HoldsSplitQueue
664
              choices:
670
              choices:
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt (+12 lines)
Lines 72-77 Link Here
72
        <div class="listgroup">
72
        <div class="listgroup">
73
        <h3 id="item[% ITEM_DAT.itemnumber | html %]">Barcode [% ITEM_DAT.barcode | html %]  [% IF ( ITEM_DAT.notforloantext ) %][% ITEM_DAT.notforloantext | html %] [% END %]</h3>
73
        <h3 id="item[% ITEM_DAT.itemnumber | html %]">Barcode [% ITEM_DAT.barcode | html %]  [% IF ( ITEM_DAT.notforloantext ) %][% ITEM_DAT.notforloantext | html %] [% END %]</h3>
74
74
75
        [% IF ITEM_DAT.itemlost and ITEM_DAT.waiting_holds > 0  %]
76
            <div class="dialog alert">
77
                This item has a waiting item-level hold on it, and has been marked as lost.<br>
78
                <form action="updateitem.pl" method="post" class="inline">
79
                    <input type="hidden" name="itemnumber" value="[% ITEM_DAT.itemnumber %]">
80
                    <input type="hidden" name="biblionumber" value="[% ITEM_DAT.biblionumber %]">
81
                    <button type="submit" name="reverthold" value="1" class="btn btn-default"><i class="fa fa-undo"></i> Revert waiting status</button>
82
                    <button type="submit" name="cancelhold" value="1" class="btn btn-default"><i class="fa fa-times"></i> Cancel hold</button>
83
                </form>
84
            </div>
85
        [% END %]
86
75
        <h4>Item information [% IF ( CAN_user_editcatalogue_edit_catalogue ) %][% UNLESS ( ITEM_DAT.nomod ) %]
87
        <h4>Item information [% IF ( CAN_user_editcatalogue_edit_catalogue ) %][% UNLESS ( ITEM_DAT.nomod ) %]
76
         [% IF ( CAN_user_editcatalogue_edit_items ) %]
88
         [% IF ( CAN_user_editcatalogue_edit_items ) %]
77
            <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>
89
            <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 46-51 Link Here
46
[% 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 %]
46
[% 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 %]
47
[% 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 %]
47
[% 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 %]
48
48
49
[% IF waitinglostitems %]
50
    <div class="alert">
51
        The following item(s) have a waiting item-level hold and have been marked as lost.
52
        <ul>
53
        [% FOREACH i IN waitinglostitems %]
54
            <br>
55
            <li>Item (<i>[% i.barcode %]</i>)
56
            <form action="../catalogue/updateitem.pl" method="post" class="inline">
57
                <input type="hidden" name="itemnumber" value="[% i.itemnumber %]">
58
                <input type="hidden" name="biblionumber" value="[% i.biblionumber %]">
59
                <input type="hidden" name="redirecturl" value="../cataloguing/additem.pl?biblionumber=">
60
                <button type="submit" name="reverthold" value="1" class="btn btn-default"><i class="fa fa-undo"></i> Revert waiting status</button>
61
                <button type="submit" name="cancelhold" value="1" class="btn btn-default"><i class="fa fa-times"></i> Cancel hold</button></li>
62
            </form>
63
        [% END %]
64
        </ul>
65
    </div>
66
[% END %]
67
49
<div id="cataloguing_additem_itemlist">
68
<div id="cataloguing_additem_itemlist">
50
    [% IF ( item_loop ) %]
69
    [% IF ( item_loop ) %]
51
        [% SET date_fields = [ 'dateaccessioned', 'onloan', 'datelastseen', 'datelastborrowed', 'replacementpricedate' ] %]
70
        [% 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 => 66;
20
use Test::More tests => 67;
21
use Test::MockModule;
21
use Test::MockModule;
22
use Test::Warn;
22
use Test::Warn;
23
23
Lines 1218-1223 subtest 'MergeHolds' => sub { Link Here
1218
    is( $biblio_2->holds->count, 1, 'Hold has been transferred' );
1218
    is( $biblio_2->holds->count, 1, 'Hold has been transferred' );
1219
};
1219
};
1220
1220
1221
subtest 'Waiting item is marked as lost' => sub {
1222
1223
    plan tests => 15;
1224
1225
    # Set up data
1226
    my $biblio = $builder->build_sample_biblio();
1227
    my $item = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
1228
    my $patron = $builder->build_object({ class => 'Koha::Patrons' });
1229
1230
    ## TEST 1: BIBLIO-LEVEL HOLD
1231
1232
    # place biblio-level hold
1233
    my $reserve_id = AddReserve({
1234
        branchcode => $item->homebranch,
1235
        borrowernumber => $patron->borrowernumber,
1236
        biblionumber => $biblio->biblionumber,
1237
    });
1238
    my $hold = Koha::Holds->find( $reserve_id );
1239
1240
    is( $hold->item_level_hold, 0, 'Biblio-level hold placed' );
1241
1242
    # set hold as waiting and assign item
1243
    $hold->set_waiting;
1244
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1245
1246
    # mark item in biblio as lost
1247
    $item->itemlost( 1 )->store;
1248
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1249
1250
    # do test
1251
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1252
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1253
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1254
1255
    # enable preference
1256
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1257
1258
    # try again
1259
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1260
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1261
    $hold = Koha::Holds->find( $reserve_id );
1262
    is( $hold->found, undef, 'Hold waiting status has been reverted because RevertLostBibLevelHolds is enabled' );
1263
1264
    # clean up
1265
    $hold->cancel;
1266
    $item->itemlost( 0 );
1267
1268
    ## TEST 2: ITEM-LEVEL HOLD, REVERT
1269
1270
    # place item-level hold
1271
    $reserve_id = AddReserve({
1272
        branchcode => $item->homebranch,
1273
        borrowernumber => $patron->borrowernumber,
1274
        biblionumber => $biblio->biblionumber,
1275
        itemnumber => $item->itemnumber
1276
    });
1277
    $hold = Koha::Holds->find( $reserve_id );
1278
1279
    is( $hold->item_level_hold, 1, 'Item-level hold placed' );
1280
1281
    # set hold as waiting and assign item
1282
    $hold->set_waiting;
1283
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1284
1285
    # mark item in biblio as lost
1286
    $item->itemlost( 1 );
1287
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1288
1289
    # do test
1290
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1291
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1292
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1293
1294
    # enable preference
1295
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1296
1297
    # try again
1298
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1299
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1300
    $hold = Koha::Holds->find( $reserve_id );
1301
    is( $hold->found, undef, 'Hold waiting status has been reverted because RevertLostBibLevelHolds is enabled, and we chose not to cancel' );
1302
1303
    # clean up
1304
    $hold->cancel;
1305
    $item->itemlost( 0 );
1306
1307
    # TEST 3: ITEM-LEVEL HOLD, CANCEL
1308
1309
    # place item-level hold
1310
    $reserve_id = AddReserve({
1311
        branchcode => $item->homebranch,
1312
        borrowernumber => $patron->borrowernumber,
1313
        biblionumber => $biblio->biblionumber,
1314
        itemnumber => $item->itemnumber
1315
    });
1316
    $hold = Koha::Holds->find( $reserve_id );
1317
1318
    is( $hold->item_level_hold, 1, 'Item-level hold placed' );
1319
1320
    # set hold as waiting and assign item
1321
    $hold->set_waiting;
1322
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1323
1324
    # mark item in biblio as lost
1325
    $item->itemlost( 1 );
1326
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1327
1328
    # do test
1329
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1330
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 1 });
1331
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1332
1333
    # enable preference
1334
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1335
1336
    # try again
1337
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1338
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 1 });
1339
    $hold = Koha::Holds->find( $reserve_id );
1340
    is( Koha::Holds->find( $reserve_id ), undef, 'Hold has been cancelled with RevertLostBibLevelHolds enabled' );
1341
};
1342
1221
sub count_hold_print_messages {
1343
sub count_hold_print_messages {
1222
    my $message_count = $dbh->selectall_arrayref(q{
1344
    my $message_count = $dbh->selectall_arrayref(q{
1223
        SELECT COUNT(*)
1345
        SELECT COUNT(*)
1224
- 

Return to bug 20844