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

(-)a/C4/Circulation.pm (+19 lines)
Lines 3825-3830 sub LostItem{ Link Here
3825
        Koha::Items->find($itemnumber)->holdingbranch($frombranch)->store({ skip_record_index => $params->{skip_record_index} });
3825
        Koha::Items->find($itemnumber)->holdingbranch($frombranch)->store({ skip_record_index => $params->{skip_record_index} });
3826
    }
3826
    }
3827
    my $transferdeleted = DeleteTransfer($itemnumber);
3827
    my $transferdeleted = DeleteTransfer($itemnumber);
3828
3829
    # RevertLostBibLevelHolds
3830
    my $hold = Koha::Holds->find({ itemnumber => $itemnumber, found => { '!=' =>  undef } });
3831
    if ( C4::Context->preference('RevertLostBibLevelHolds') && defined $hold ){
3832
        # syspref is enabled and there is a waiting hold for this item
3833
        if ( $hold->item_level_hold && defined $params->{cancel_reserve} ){
3834
            if ( $params->{cancel_reserve} ){
3835
                # cancel item-level hold
3836
                $hold->cancel;
3837
            } else { # eq 'revert'
3838
                # revert item-level hold's waiting status
3839
                $hold = C4::Reserves::RevertWaitingStatus({ itemnumber => $itemnumber });
3840
            }
3841
        }
3842
        if ( !$hold->item_level_hold ){
3843
            # revert biblio-level hold's waiting status
3844
            $hold = C4::Reserves::RevertWaitingStatus({ itemnumber => $itemnumber });
3845
        }
3846
    }
3828
}
3847
}
3829
3848
3830
sub GetOfflineOperations {
3849
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 995-1000 foreach my $tag ( keys %{$tagslib}){ Link Here
995
}
995
}
996
@loop_data = sort {$a->{subfield} cmp $b->{subfield} } @loop_data;
996
@loop_data = sort {$a->{subfield} cmp $b->{subfield} } @loop_data;
997
997
998
if ( C4::Context->preference('RevertLostBibLevelHolds') ){
999
    my @lostitems = Koha::Items->search({ biblionumber => $biblionumber, itemlost => { '!=' => 0 } });
1000
    # find if any of these lost items have waiting holds
1001
    my @waitinglostitems;
1002
    foreach my $lostitem ( @lostitems ){
1003
        if ( $lostitem->holds({ found => 'W', item_level_hold => 1 })->count > 0 ){
1004
            push( @waitinglostitems, $lostitem );
1005
        }
1006
    }
1007
    $template->param( waitinglostitems => \@waitinglostitems );
1008
}
1009
998
# what's the next op ? it's what we are not in : an add if we're editing, otherwise, and edit.
1010
# what's the next op ? it's what we are not in : an add if we're editing, otherwise, and edit.
999
$template->param(
1011
$template->param(
1000
    biblionumber => $biblionumber,
1012
    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 565-570 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
565
('ReturnBeforeExpiry','0',NULL,'If ON, checkout will be prevented if returndate is after patron card expiry','YesNo'),
565
('ReturnBeforeExpiry','0',NULL,'If ON, checkout will be prevented if returndate is after patron card expiry','YesNo'),
566
('ReturnLog','1',NULL,'If ON, enables the circulation (returns) log','YesNo'),
566
('ReturnLog','1',NULL,'If ON, enables the circulation (returns) log','YesNo'),
567
('ReturnpathDefault','',NULL,'Use this email address as return path or bounce address for undeliverable emails','Free'),
567
('ReturnpathDefault','',NULL,'Use this email address as return path or bounce address for undeliverable emails','Free'),
568
('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'),
568
('reviewson','1','','If ON, enables patron reviews of bibliographic records in the OPAC','YesNo'),
569
('reviewson','1','','If ON, enables patron reviews of bibliographic records in the OPAC','YesNo'),
569
('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'),
570
('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'),
570
('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'),
571
('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'),
Lines 589-594 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
589
('SelfCheckReceiptPrompt','1','NULL','If ON, print receipt dialog pops up when self checkout is finished','YesNo'),
590
('SelfCheckReceiptPrompt','1','NULL','If ON, print receipt dialog pops up when self checkout is finished','YesNo'),
590
('SelfCheckTimeout','120','','Define the number of seconds before the Web-based Self Checkout times out a patron','Integer'),
591
('SelfCheckTimeout','120','','Define the number of seconds before the Web-based Self Checkout times out a patron','Integer'),
591
('SendAllEmailsTo','',NULL,'All emails will be redirected to this email if it is not empty','free'),
592
('SendAllEmailsTo','',NULL,'All emails will be redirected to this email if it is not empty','free'),
593
('SendLostHoldNotices', '0', NULL, 'Send a notice to a borrower if their reserved and waiting item is marked as lost', 'YesNo'),
592
('SeparateHoldings','0',NULL,'Separate current branch holdings from other holdings','YesNo'),
594
('SeparateHoldings','0',NULL,'Separate current branch holdings from other holdings','YesNo'),
593
('SeparateHoldingsBranch','homebranch','homebranch|holdingbranch','Branch used to separate holdings','Choice'),
595
('SeparateHoldingsBranch','homebranch','homebranch|holdingbranch','Branch used to separate holdings','Choice'),
594
('SessionRestrictionByIP','1','Check for change in remote IP address for session security. Disable only when remote IP address changes frequently.','','YesNo'),
596
('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 645-650 Circulation: Link Here
645
            - the last patron to return an item. This setting is independent of the opacreadinghistory and AnonymousPatron system preferences.
645
            - the last patron to return an item. This setting is independent of the opacreadinghistory and AnonymousPatron system preferences.
646
    Holds policy:
646
    Holds policy:
647
        -
647
        -
648
            - pref: RevertLostBibLevelHolds
649
              choices:
650
                  yes: Revert
651
                  no: "Don't revert"
652
            - waiting status of a hold if the allocated item is marked as Lost.
653
        -
648
            - In the staff interface, split the holds queue into separate tables by
654
            - In the staff interface, split the holds queue into separate tables by
649
            - pref: HoldsSplitQueue
655
            - pref: HoldsSplitQueue
650
              choices:
656
              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 => 65;
20
use Test::More tests => 66;
21
use Test::MockModule;
21
use Test::MockModule;
22
use Test::Warn;
22
use Test::Warn;
23
23
Lines 1190-1195 subtest 'AllowHoldOnPatronPossession test' => sub { Link Here
1190
       'Patron can place hold on an item loaned to itself');
1190
       'Patron can place hold on an item loaned to itself');
1191
};
1191
};
1192
1192
1193
subtest 'Waiting item is marked as lost' => sub {
1194
1195
    plan tests => 15;
1196
1197
    # Set up data
1198
    my $biblio = $builder->build_sample_biblio();
1199
    my $item = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
1200
    my $patron = $builder->build_object({ class => 'Koha::Patrons' });
1201
1202
    ## TEST 1: BIBLIO-LEVEL HOLD
1203
1204
    # place biblio-level hold
1205
    my $reserve_id = AddReserve({
1206
        branchcode => $item->homebranch,
1207
        borrowernumber => $patron->borrowernumber,
1208
        biblionumber => $biblio->biblionumber,
1209
    });
1210
    my $hold = Koha::Holds->find( $reserve_id );
1211
1212
    is( $hold->item_level_hold, 0, 'Biblio-level hold placed' );
1213
1214
    # set hold as waiting and assign item
1215
    $hold->set_waiting;
1216
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1217
1218
    # mark item in biblio as lost
1219
    $item->itemlost( 1 )->store;
1220
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1221
1222
    # do test
1223
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1224
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1225
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1226
1227
    # enable preference
1228
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1229
1230
    # try again
1231
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1232
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1233
    $hold = Koha::Holds->find( $reserve_id );
1234
    is( $hold->found, undef, 'Hold waiting status has been reverted because RevertLostBibLevelHolds is enabled' );
1235
1236
    # clean up
1237
    $hold->cancel;
1238
    $item->itemlost( 0 );
1239
1240
    ## TEST 2: ITEM-LEVEL HOLD, REVERT
1241
1242
    # place item-level hold
1243
    $reserve_id = AddReserve({
1244
        branchcode => $item->homebranch,
1245
        borrowernumber => $patron->borrowernumber,
1246
        biblionumber => $biblio->biblionumber,
1247
        itemnumber => $item->itemnumber
1248
    });
1249
    $hold = Koha::Holds->find( $reserve_id );
1250
1251
    is( $hold->item_level_hold, 1, 'Item-level hold placed' );
1252
1253
    # set hold as waiting and assign item
1254
    $hold->set_waiting;
1255
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1256
1257
    # mark item in biblio as lost
1258
    $item->itemlost( 1 );
1259
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1260
1261
    # do test
1262
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1263
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1264
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1265
1266
    # enable preference
1267
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1268
1269
    # try again
1270
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1271
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 0 });
1272
    $hold = Koha::Holds->find( $reserve_id );
1273
    is( $hold->found, undef, 'Hold waiting status has been reverted because RevertLostBibLevelHolds is enabled, and we chose not to cancel' );
1274
1275
    # clean up
1276
    $hold->cancel;
1277
    $item->itemlost( 0 );
1278
1279
    # TEST 3: ITEM-LEVEL HOLD, CANCEL
1280
1281
    # place item-level hold
1282
    $reserve_id = AddReserve({
1283
        branchcode => $item->homebranch,
1284
        borrowernumber => $patron->borrowernumber,
1285
        biblionumber => $biblio->biblionumber,
1286
        itemnumber => $item->itemnumber
1287
    });
1288
    $hold = Koha::Holds->find( $reserve_id );
1289
1290
    is( $hold->item_level_hold, 1, 'Item-level hold placed' );
1291
1292
    # set hold as waiting and assign item
1293
    $hold->set_waiting;
1294
    $hold->set({ itemnumber => $item->itemnumber, priority => 0 })->store;
1295
1296
    # mark item in biblio as lost
1297
    $item->itemlost( 1 );
1298
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1299
1300
    # do test
1301
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 0 );
1302
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 1 });
1303
    is( $hold->found, 'W', 'Hold is still waiting even though the item is lost because RevertLostBibLevelHolds is disabled' );
1304
1305
    # enable preference
1306
    t::lib::Mocks::mock_preference( 'RevertLostBibLevelHolds', 1 );
1307
1308
    # try again
1309
    is( $item->itemlost, 1, 'Item assigned to waiting hold has been marked lost');
1310
    C4::Circulation::LostItem( $item->itemnumber, 'test', 0, { cancel_reserve => 1 });
1311
    $hold = Koha::Holds->find( $reserve_id );
1312
    is( Koha::Holds->find( $reserve_id ), undef, 'Hold has been cancelled with RevertLostBibLevelHolds enabled' );
1313
};
1314
1193
sub count_hold_print_messages {
1315
sub count_hold_print_messages {
1194
    my $message_count = $dbh->selectall_arrayref(q{
1316
    my $message_count = $dbh->selectall_arrayref(q{
1195
        SELECT COUNT(*)
1317
        SELECT COUNT(*)
1196
- 

Return to bug 20844