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

(-)a/C4/Reserves.pm (+34 lines)
Lines 1115-1120 sub ModReserve { Link Here
1115
    if ( $rank eq "del" ) {
1115
    if ( $rank eq "del" ) {
1116
        $hold->cancel({ cancellation_reason => $cancellation_reason });
1116
        $hold->cancel({ cancellation_reason => $cancellation_reason });
1117
    }
1117
    }
1118
    elsif ( $rank eq "recall" ) {
1119
        if ( !$biblionumber ) {
1120
            $biblionumber = $hold->biblionumber;
1121
        }
1122
        my $biblio = Koha::Biblios->find( $biblionumber );
1123
        if ( !$borrowernumber ) {
1124
            $borrowernumber = $hold->borrowernumber;
1125
        }
1126
        my $patron = Koha::Patrons->find( $borrowernumber );
1127
        if ( $hold->item_level_hold ) {
1128
            if ( $hold->item->can_be_recalled({ patron => $patron, hold_convert => 1 }) ) {
1129
                my ( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall({
1130
                    patron => $patron,
1131
                    biblio => $biblio,
1132
                    branchcode => $branchcode,
1133
                    expirationdate => $date,
1134
                    interface => 'intranet',
1135
                    item => $hold->item,
1136
                });
1137
                $hold->cancel({ cancellation_reason => 'RECALLED' });
1138
            }
1139
        } else {
1140
            if ( $biblio->can_be_recalled({ patron => $patron, hold_convert => 1 }) ) {
1141
                my ( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall({
1142
                    patron => $patron,
1143
                    biblio => $biblio,
1144
                    branchcode => $branchcode,
1145
                    expirationdate => $date,
1146
                    interface => 'intranet',
1147
                });
1148
                $hold->cancel({ cancellation_reason => 'RECALLED' });
1149
            }
1150
        }
1151
    }
1118
    elsif ($hold->found && $hold->priority eq '0' && $date) {
1152
    elsif ($hold->found && $hold->priority eq '0' && $date) {
1119
        logaction( 'HOLDS', 'MODIFY', $hold->reserve_id, $hold )
1153
        logaction( 'HOLDS', 'MODIFY', $hold->reserve_id, $hold )
1120
            if C4::Context->preference('HoldsLog');
1154
            if C4::Context->preference('HoldsLog');
(-)a/Koha/Biblio.pm (-1 / +3 lines)
Lines 1283-1288 sub recalls { Link Here
1283
1283
1284
Does biblio-level checks and returns the items attached to this biblio that are available for recall
1284
Does biblio-level checks and returns the items attached to this biblio that are available for recall
1285
1285
1286
if hold_convert $param is included, this is to say that this a check to convert a hold to a recall, so we should not check for an existing hold.
1287
1286
=cut
1288
=cut
1287
1289
1288
sub can_be_recalled {
1290
sub can_be_recalled {
Lines 1308-1314 sub can_be_recalled { Link Here
1308
    my @all_itemnumbers;
1310
    my @all_itemnumbers;
1309
    foreach my $item ( @all_items ) {
1311
    foreach my $item ( @all_items ) {
1310
        push( @all_itemnumbers, $item->itemnumber );
1312
        push( @all_itemnumbers, $item->itemnumber );
1311
        if ( $item->can_be_recalled({ patron => $patron }) ) {
1313
        if ( $item->can_be_recalled({ patron => $patron, hold_convert => $params->{hold_convert} }) ) {
1312
            push( @itemtypes, $item->effective_itemtype );
1314
            push( @itemtypes, $item->effective_itemtype );
1313
            push( @itemnumbers, $item->itemnumber );
1315
            push( @itemnumbers, $item->itemnumber );
1314
            push( @items, $item );
1316
            push( @items, $item );
(-)a/Koha/Item.pm (-1 / +5 lines)
Lines 1840-1845 sub recall { Link Here
1840
1840
1841
Does item-level checks and returns if items can be recalled by this borrower
1841
Does item-level checks and returns if items can be recalled by this borrower
1842
1842
1843
if hold_convert $param is included, this is to say that this a check to convert a hold to a recall, so we should not check for an existing hold.
1844
1843
=cut
1845
=cut
1844
1846
1845
sub can_be_recalled {
1847
sub can_be_recalled {
Lines 1891-1897 sub can_be_recalled { Link Here
1891
        return 0 if ( Koha::Checkouts->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1893
        return 0 if ( Koha::Checkouts->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1892
1894
1893
        # check if this patron has already reserved this item
1895
        # check if this patron has already reserved this item
1894
        return 0 if ( Koha::Holds->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1896
        unless ( $params->{hold_convert} ) {
1897
            return 0 if ( Koha::Holds->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1898
        }
1895
    }
1899
    }
1896
1900
1897
    # check item availability
1901
    # check item availability
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/holds_table.inc (+6 lines)
Lines 64-69 Link Here
64
                            <option value="[% hold.priority | html %]" selected="selected">[% this_priority | html %]</option>
64
                            <option value="[% hold.priority | html %]" selected="selected">[% this_priority | html %]</option>
65
                        [% END %]
65
                        [% END %]
66
                            <option value="del">del</option>
66
                            <option value="del">del</option>
67
                            [% IF Koha.Preference('UseRecalls') AND Koha.Preference('RecallsInterface') != 'opac' %]
68
                            <option value="recall">recall</option>
69
                            [% END %]
67
                        </select>
70
                        </select>
68
                    [% ELSE %]
71
                    [% ELSE %]
69
                        <input type="hidden" name="rank-request" class="rank-request" value="[% hold.priority | html %]" data-hold-id="[% hold.reserve_id | html %]">
72
                        <input type="hidden" name="rank-request" class="rank-request" value="[% hold.priority | html %]" data-hold-id="[% hold.reserve_id | html %]">
Lines 79-84 Link Here
79
                        [% ELSE %]
82
                        [% ELSE %]
80
                            <option value="[% hold.priority | html %]" selected="selected">[% this_priority | html %]</option>
83
                            <option value="[% hold.priority | html %]" selected="selected">[% this_priority | html %]</option>
81
                        [% END %]
84
                        [% END %]
85
                            [% IF Koha.Preference('UseRecalls') AND Koha.Preference('RecallsInterface') != 'opac' %]
86
                            <option value="recall">recall</option>
87
                            [% END %]
82
                        </select>
88
                        </select>
83
                    [% END %]
89
                    [% END %]
84
                [% ELSE %]
90
                [% ELSE %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/recalls/request.tt (-17 / +246 lines)
Lines 2-9 Link Here
2
[% USE Asset %]
2
[% USE Asset %]
3
[% USE Koha %]
3
[% USE Koha %]
4
[% USE KohaDates %]
4
[% USE KohaDates %]
5
[% USE TablesSettings %]
6
[% USE Branches %]
7
[% USE Categories %]
8
[% USE AuthorisedValues %]
9
[% USE ItemTypes %]
5
[% SET footerjs = 1 %]
10
[% SET footerjs = 1 %]
6
[% INCLUDE 'doc-head-open.inc' %]
11
[% INCLUDE 'doc-head-open.inc' %]
12
[% SET libraries = Branches.all %]
13
[% SET categories = Categories.all.unblessed %]
14
[% SET columns = ['name', 'cardnumber', 'dateofbirth', 'category', 'branch', 'address', 'phone'] %]
15
[% PROCESS "patron-search.inc" %]
7
<title>Confirm recalls &rsaquo; Recalls &rsaquo; Koha</title>
16
<title>Confirm recalls &rsaquo; Recalls &rsaquo; Koha</title>
8
[% INCLUDE 'doc-head-close.inc' %]
17
[% INCLUDE 'doc-head-close.inc' %]
9
</head>
18
</head>
Lines 12-47 Link Here
12
    [% INCLUDE 'circ-search.inc' %]
21
    [% INCLUDE 'circ-search.inc' %]
13
[% END %]
22
[% END %]
14
23
15
<div id="breadcrumbs">
24
<nav id="breadcrumbs" aria-label="Breadcrumb" class="breadcrumb">
16
    <a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo;
25
    <ol>
17
    <a href="/cgi-bin/koha/catalogue/search.pl">Catalog</a> &rsaquo;
26
        <li>
18
    <a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% biblio.biblionumber | uri %]">[% biblio.title | html %]</a> &rsaquo;
27
            <a href="/cgi-bin/koha/mainpage.pl">Home</a>
19
    Confirm recalls on [% biblio.title | html %]
28
        </li>
20
</div>
29
        <li>
30
            <a href="/cgi-bin/koha/catalogue/search.pl">Catalog</a>
31
        </li>
32
        <li>
33
            [% INCLUDE 'biblio-title.inc' link =1 %]
34
        </li>
35
        <li>
36
            <a href="#" aria-current="page">
37
                Place a recall
38
            </a>
39
        </li>
40
    </ol>
41
</nav>
21
42
22
<div class="main container-fluid">
43
<div class="main container-fluid">
23
<div class="row">
44
<div class="row">
24
<div class="col-sm-10 col-sm-push-2">
45
<div class="col-sm-10 col-sm-push-2">
25
<main>
46
<main>
26
47
27
    <h1>Existing recalls</h1>
48
    <h1>Recalls</h1>
28
49
29
    [% IF Koha.Preference('UseRecalls') %]
50
    <h2>Place a recall on [% INCLUDE 'biblio-title.inc' link = 1 %]</h2>
30
        [% IF recalls.count %]
51
31
            <form method="post" action="/cgi-bin/koha/recalls/request.pl">
52
    [% IF patron %]
32
                <input type="hidden" name="op" value="cancel_multiple_recalls">
53
54
        [% IF error %]
55
            <div class="dialog alert">
56
                Unable to place a recall. Check your circulation rules.
57
            </div>
58
        [% END %]
59
60
        <fieldset class="rows">
61
            <legend>Recall details</legend>
62
            <form id="recallform" action="/cgi-bin/koha/recalls/request.pl" method="post">
33
                <input type="hidden" name="biblionumber" value="[% biblio.biblionumber | html %]">
63
                <input type="hidden" name="biblionumber" value="[% biblio.biblionumber | html %]">
34
                <input type="checkbox" id="select_all"> <span id="select_all_text">Select all</span>
64
35
                [% INCLUDE 'recalls.inc' %]
65
                <ol>
66
                    <li>
67
                        <label for="borrowernumber">Patron:</label> <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% patron.borrowernumber | uri %]">[% INCLUDE 'patron-title.inc' %]</a>
68
                        <input type="hidden" name="borrowernumber" value="[% patron.borrowernumber | html %]">
69
                    </li>
70
71
                    [% UNLESS ( single_branch_mode ) %]<li>
72
                        <label for="pickup">Pick up at:</label>
73
                        <select name="pickup" id="pickup">
74
                            [% FOREACH branch IN branches %]
75
                                [% IF branch.branchcode == patron.branchcode %]
76
                                    <option value="[% branch.branchcode | html %]" selected>[% Branches.GetName( branch.branchcode ) | html %]</option>
77
                                [% ELSE %]
78
                                    <option value="[% branch.branchcode | html %]">[% Branches.GetName( branch.branchcode ) | html %]</option>
79
                                [% END %]
80
                            [% END %]
81
                        </select>
82
                    </li>[% END %]
83
84
                    <li>
85
                        <label for="expirationdate">Recall not needed after:</label> <input type="text" name="expirationdate" id="expirationdate" size="20" class="flatpickr">
86
                    </li>
87
                    <li>
88
                        <label for="type">Recall next available item</label> <input type="checkbox" name="type" id="type" checked>
89
                    </li>
90
                </ol>
91
92
                <fieldset class="action">
93
                    <input type="hidden" name="op" value="request">
94
                    <input type="hidden" name="biblionumber" value="[% biblio.biblionumber | html %]">
95
                    [% IF error %]
96
                        <input type="submit" class="btn btn-default" value="Place recall" disabled="disabled">
97
                    [% ELSE %]
98
                        <input type="submit" class="btn btn-default" value="Place recall">
99
                    [% END %]
100
                    <a href="/cgi-bin/koha/recalls/request.pl?biblionumber=[% biblio.biblionumber | uri %]" class="cancel">Cancel</a>
101
                </fieldset>
102
103
                <table id="items">
104
                    <caption>Place a recall on a specific item</caption>
105
                    <thead>
106
                    <tr>
107
                        <th>&nbsp;</th>
108
                        <th>Item type</th>
109
                        <th>Barcode</th>
110
                        [% UNLESS ( single_branch_mode ) %]
111
                            <th>Home library</th>
112
                            <th>Last location</th>
113
                        [% END %]
114
                        <th>Collection</th>
115
                        <th>Call number</th>
116
                        <th>Copy number</th>
117
                        <th>Vol no.</th>
118
                        <th>Information</th>
119
                    </tr>
120
                    </thead>
121
                    <tbody>
122
                    [% FOREACH item IN items %]<tr>
123
                    <td>
124
                        [% IF item.can_be_recalled( patron => patron ) %]
125
                            <input type="radio" class="itemnumber" name="itemnumber" value="[% item.itemnumber | html %]">
126
                        [% ELSE %]
127
                            <span class="error">
128
                                <i class="fa fa-times fa-lg" title="Cannot be recalled"></i>
129
                            </span>
130
                        [% END %]
131
                        </td>
132
                        <td>[% ItemTypes.GetDescription( item.effective_itemtype ) | html %]</td>
133
                        <td>[% item.barcode | html %]</td>
134
                        [% UNLESS ( single_branch_mode ) %]
135
                            <td>[% Branches.GetName( item.homebranch ) | html %]</td>
136
                            <td>[% Branches.GetName( item.holdingbranch) | html %]</td>
137
                        [% END %]
138
                        <td>[% AuthorisedValues.GetByCode( 'CCODE', item.ccode, 1 ) | html %]</td>
139
                        <td>[% item.itemcallnumber | html %]</td>
140
                        <td>[% item.copynumber | html %]</td>
141
                        <td>[% item.enumchron | html %]</td>
142
                        <td>
143
                            [% IF ( item.checkout ) %]
144
                                <span class="checkedout">Due [% item.checkout.date_due | $KohaDates %]</span>
145
                            [% ELSIF ( item.get_transfer ) %]
146
                                <span class="intransit">In transit from [% Branches.GetName( item.get_transfer.frombranch ) | html %] to [% Branches.GetName( item.get_transfer.tobranch ) | html %] since [% item.get_transfer.datesent | $KohaDates %]</span>
147
                            [% END %]
148
                            [% IF ( item.itemlost || item.withdrawn ) %]
149
                                <span class="lost">Unavailable (lost or missing)</span>
150
                            [% END %]
151
                            [% IF ( item.notforloan ) %]
152
                                <span class="notforloan">Not for loan ([% item.notforloan | html %])</span>
153
                            [% END %]
154
                            [% hold = item.current_holds.next %]
155
                            [% IF ( item.recall ) %]
156
                                <span class="waiting">
157
                                [% IF ( item.recall.waiting_date ) %]
158
                                    Waiting for patron at [% Branches.GetName( item.recall.pickup_library_id ) | html %] since [% item.recall.waiting_date | $KohaDates %].
159
                                [% ELSIF ( item.recall.item_id == item.itemnumber ) %]
160
                                    Recalled by patron expected at [% Branches.GetName( item.recall.pickup_library_id ) | html %] since [% item.recall.created_date | $KohaDates %].
161
                                [% END %]
162
                                </span>
163
                            [% ELSIF ( hold.waitingdate ) %]
164
                                <span class="waiting">
165
                                    Waiting for patron at [% Branches.GetName( hold.branchcode ) | html %] since [% hold.waitingdate | $KohaDates %].
166
                                </span>
167
                            [% ELSIF ( hold.borrowernumber == logged_in_user.borrowernumber ) %]
168
                                <span class="waiting">
169
                                    Patron has already placed a <a href="/cgi-bin/koha/circ/circulation.pl?borrowernumber=[% patron.borrowernumber | uri %]#reserves">reserve</a> on this item.
170
                                </span>
171
                            [% END # / IF ( item.recall or hold ) %]
172
                        </td>
173
                    </tr>[% END %]
174
                    </tbody>
175
                </table>
176
36
                <fieldset class="action">
177
                <fieldset class="action">
37
                    <button type="submit" id="cancel_selected" class="btn btn-default btn-sm">Cancel selected recalls</button>
178
                    <input type="hidden" name="op" value="request">
179
                    <input type="hidden" name="biblionumber" value="[% biblio.biblionumber | html %]">
180
                    [% IF error %]
181
                        <input type="submit" class="btn btn-default" value="Place recall" disabled="disabled">
182
                    [% ELSE %]
183
                        <input type="submit" class="btn btn-default" value="Place recall">
184
                    [% END %]
185
                    <a href="/cgi-bin/koha/recalls/request.pl?biblionumber=[% biblio.biblionumber | uri %]" class="cancel">Cancel</a>
38
                </fieldset>
186
                </fieldset>
39
            </form>
187
            </form>
188
189
        </fieldset>
190
191
    [% ELSE %]
192
193
        [% IF Koha.Preference('RecallsInterface') == 'opac' %]
194
        <div class="dialog alert">
195
            Due to the <a href="/cgi-bin/koha/admin/preferences.pl?op=search&searchfield=RecallsInterface">RecallsInterface system preference</a>, recalls cannot be placed through the staff client.
196
        </div>
40
        [% ELSE %]
197
        [% ELSE %]
41
            <div class="dialog message">No recalls have been made.</div>
198
199
        <fieldset>
200
            <h2>Search patrons</h2>
201
            <div id="circ_recalls_select" class="toptabs">
202
                <ul class="nav nav-tabs" role="tablist">
203
                    <li role="presentation" class="active"><a href="#recalls_patronsearch_pane" aria-controls="recalls_patronsearch_pane" role="tab" data-toggle="tab">Patrons</a></li>
204
                </ul>
205
                <div class="tab-content">
206
                    <div id="recalls_patronsearch_pane" role="tabpanel" class="tab-pane active">
207
                        [% PROCESS patron_search_filters_simple %]
208
                        [% PROCESS patron_search_table table_id => 'table_borrowers', open_on_row_click => 1 %]
209
                    </div>
210
                </div> <!-- /.tab-content -->
211
            </div>
212
        </fieldset>
213
42
        [% END %]
214
        [% END %]
43
    [% ELSE %]
215
44
        <div class="dialog message">Recalls have not been enabled. Enable the <a href="/cgi-bin/koha/admin/preferences.pl?tab=circulation">UseRecalls</a> system preference to use recalls.</div>
216
        <h2>Existing recalls</h2>
217
218
        [% IF Koha.Preference('UseRecalls') %]
219
            [% IF recalls.count %]
220
                <form method="post" action="/cgi-bin/koha/recalls/request.pl">
221
                    <input type="hidden" name="op" value="cancel_multiple_recalls">
222
                    <input type="checkbox" id="select_all"> <span id="select_all_text">Select all</span>
223
                    [% INCLUDE 'recalls.inc' %]
224
                    <fieldset class="action">
225
                        <button type="submit" id="cancel_selected" class="btn btn-default btn-sm">Cancel selected recalls</button>
226
                    </fieldset>
227
                </form>
228
            [% ELSE %]
229
                <div class="dialog message">No recalls have been made.</div>
230
            [% END %]
231
        [% ELSE %]
232
            <div class="dialog message">Recalls have not been enabled. Enable the <a href="/cgi-bin/koha/admin/preferences.pl?tab=circulation">UseRecalls</a> system preference to use recalls.</div>
233
        [% END %]
234
45
    [% END %]
235
    [% END %]
46
236
47
</main>
237
</main>
Lines 60-65 Link Here
60
    [% Asset.js("js/recalls.js") | $raw %]
250
    [% Asset.js("js/recalls.js") | $raw %]
61
    [% INCLUDE 'datatables.inc' %]
251
    [% INCLUDE 'datatables.inc' %]
62
    [% INCLUDE 'columns_settings.inc' %]
252
    [% INCLUDE 'columns_settings.inc' %]
253
    [% INCLUDE 'calendar.inc' %]
254
    [% SET url_biblio_params = "biblionumber=" _ biblio.biblionumber %]
255
    [% PROCESS patron_search_js table_id => 'table_borrowers', categories => categories, libraries => libraries, extended_attribute_types => attribute_type_codes, columns => columns, open_on_row_click => 1, on_click_url => '/cgi-bin/koha/recalls/request.pl?' _ url_biblio_params, redirect_if_one_result => 1, redirect_url => '/cgi-bin/koha/recalls/request.pl?' _ url_biblio_params, redirect_if_attribute_equal => 'cardnumber' %]
256
    <script>
257
        table_settings = [% TablesSettings.GetColumns( 'circ', 'circulation', 'table_borrowers', 'json' ) | $raw %];
258
        $(document).ready(function(){
259
            $("#recalls_patronsearch").on("submit", filter);
260
            [% UNLESS patron %]
261
                [% IF ( PatronAutoComplete ) %]
262
                    patron_autocomplete($("#search_patron_filter"), { 'link-to': 'recall', 'url-params': '[% url_biblio_params | url %]' });
263
                [% END %]
264
            [% END %]
265
            $("#items").dataTable($.extend(true, {}, dataTablesDefaults, {
266
                'bPaginate': false,
267
                "sDom": '<"top pager"ilf>t',
268
            }));
269
270
            //Override fieldset styling for dataTables search box
271
            $("div.top.pager").css("margin-left","1em");
272
            $(".dataTables_filter label").css({
273
                "width":"auto",
274
                "margin-right":"0em"
275
            });
276
            $("#type").click(function() {
277
                if(this.checked){
278
                    $("input[name=itemnumber]").each(function() {
279
                        $(this).prop("checked", false);
280
                    });
281
                }
282
            });
283
            $("input[name=itemnumber]").click(function() {
284
                $("input[name=itemnumber]").each(function() {
285
                    if(this.checked){
286
                        $("#type").prop("checked", false);
287
                    }
288
                });
289
            });
290
        });
291
    </script>
63
[% END %]
292
[% END %]
64
293
65
[% INCLUDE 'intranet-bottom.inc' %]
294
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt (+8 lines)
Lines 219-224 Link Here
219
                        <p>No club with this name, please, try another</p>
219
                        <p>No club with this name, please, try another</p>
220
                    </div>
220
                    </div>
221
                [% END %]
221
                [% END %]
222
223
                [% IF ( recallerror ) %]
224
                    <div class="dialog alert">
225
                        <h3>Hold cannot be converted to a recall</h3>
226
                        <p>A recall cannot be placed on one or more holds. Check your <a href="/cgi-bin/koha/admin/smart-rules.pl">circulation and fine rules</a>.</p>
227
                    </div>
228
                [% END %]
229
222
                <fieldset>
230
                <fieldset>
223
                    [% UNLESS multi_hold %]
231
                    [% UNLESS multi_hold %]
224
                        [% IF clubcount %]
232
                        [% IF clubcount %]
(-)a/koha-tmpl/intranet-tmpl/prog/js/patron-autocomplete.js (-1 / +3 lines)
Lines 67-73 function patron_autocomplete(node, options) { Link Here
67
                ? "/cgi-bin/koha/circ/circulation.pl"
67
                ? "/cgi-bin/koha/circ/circulation.pl"
68
                : link_to == 'reserve'
68
                : link_to == 'reserve'
69
                    ? "/cgi-bin/koha/reserve/request.pl"
69
                    ? "/cgi-bin/koha/reserve/request.pl"
70
                    : "/cgi-bin/koha/members/moremember.pl";
70
                    : link_to == 'recall'
71
                        ? "/cgi-bin/koha/recalls/request.pl"
72
                        : "/cgi-bin/koha/members/moremember.pl";
71
            item.link += ( url_params ? '?' + url_params + '&' : "?" ) + 'borrowernumber=' + item.patron_id;
73
            item.link += ( url_params ? '?' + url_params + '&' : "?" ) + 'borrowernumber=' + item.patron_id;
72
        } else {
74
        } else {
73
            item.link = null;
75
            item.link = null;
(-)a/opac/opac-recall.pl (-4 / +6 lines)
Lines 36-41 my $op = $query->param('op') || ''; Link Here
36
my $biblionumber = $query->param('biblionumber');
36
my $biblionumber = $query->param('biblionumber');
37
my $biblio = Koha::Biblios->find( $biblionumber );
37
my $biblio = Koha::Biblios->find( $biblionumber );
38
38
39
if ($op eq 'cancel'){
40
    my $recall_id = $query->param('recall_id');
41
    Koha::Recalls->find( $recall_id )->set_cancelled;
42
    print $query->redirect('/cgi-bin/koha/opac-user.pl');
43
}
44
39
if ( C4::Context->preference('UseRecalls') and C4::Context->preference('RecallsInterface') ne 'staff' ) {
45
if ( C4::Context->preference('UseRecalls') and C4::Context->preference('RecallsInterface') ne 'staff' ) {
40
46
41
    my $patron = Koha::Patrons->find( $borrowernumber );
47
    my $patron = Koha::Patrons->find( $borrowernumber );
Lines 112-121 if ( C4::Context->preference('UseRecalls') and C4::Context->preference('RecallsI Link Here
112
                $error = 'failed';
118
                $error = 'failed';
113
            }
119
            }
114
        }
120
        }
115
    } elsif ($op eq 'cancel'){
116
        my $recall_id = $query->param('recall_id');
117
        Koha::Recalls->find( $recall_id )->set_cancelled;
118
        print $query->redirect('/cgi-bin/koha/opac-user.pl');
119
    }
121
    }
120
122
121
    my $branches = Koha::Libraries->search();
123
    my $branches = Koha::Libraries->search();
(-)a/recalls/request.pl (-5 / +93 lines)
Lines 38-64 my ($template, $loggedinuser, $cookie)= get_template_and_user( Link Here
38
my $op = $input->param('op') || 'list';
38
my $op = $input->param('op') || 'list';
39
my @recall_ids = $input->multi_param('recall_ids');
39
my @recall_ids = $input->multi_param('recall_ids');
40
my $biblionumber = $input->param('biblionumber');
40
my $biblionumber = $input->param('biblionumber');
41
my $recalls = Koha::Recalls->search({ biblio_id => $biblionumber, completed => 0 });
42
my $biblio = Koha::Biblios->find( $biblionumber );
41
my $biblio = Koha::Biblios->find( $biblionumber );
42
my $borrowernumber = $input->param('borrowernumber');
43
my $error = $input->param('error');
43
44
44
if ( $op eq 'cancel_multiple_recalls' ) {
45
if ( $op eq 'cancel_multiple_recalls' ) {
45
    foreach my $id ( @recall_ids ) {
46
    foreach my $id ( @recall_ids ) {
46
        Koha::Recalls->find( $id )->set_cancelled;
47
        Koha::Recalls->find( $id )->set_cancelled;
47
    }
48
    }
48
    $op = 'list'
49
}
49
}
50
elsif ( $op eq 'request' ) {
51
52
    if ( C4::Context->preference('UseRecalls') and C4::Context->preference('RecallsInterface') ne 'opac' and $borrowernumber ) {
53
        my $patron = Koha::Patrons->find( $borrowernumber );
54
55
        unless ( $biblio->can_be_recalled({ patron => $patron }) ) { $error = 'unavailable'; }
56
        my $items = Koha::Items->search({ biblionumber => $biblionumber })->as_list;
57
58
        # check if already recalled
59
        my $recalled = $biblio->recalls->filter_by_current->search({ patron_id => $borrowernumber })->count;
60
        if ( defined $recalled and $recalled > 0 ) {
61
            my $recalls_per_record = Koha::CirculationRules->get_effective_rule({
62
                categorycode => $patron->categorycode,
63
                branchcode => undef,
64
                itemtype => undef,
65
                rule_name => 'recalls_per_record'
66
            });
67
            if ( defined $recalls_per_record and $recalls_per_record->rule_value and $recalled >= $recalls_per_record->rule_value ){
68
                $error = 'duplicate';
69
            }
70
        }
71
72
        if ( !defined $error ){
73
            my $pickuploc = $input->param('pickup');
74
            my $expdate = $input->param('expirationdate');
75
            my $level = $input->param('type');
76
            my $itemnumber = $input->param('itemnumber');
77
78
            my ( $recall, $due_interval, $due_date );
79
80
            if ( !defined $level and defined $itemnumber ) {
81
                my $item = Koha::Items->find( $itemnumber );
82
                if ( $item->can_be_recalled({ patron => $patron }) ) {
83
                    ( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall({
84
                        patron => $patron,
85
                        biblio => $biblio,
86
                        branchcode => $pickuploc,
87
                        item => $item,
88
                        expirationdate => $expdate,
89
                        interface => 'staff',
90
                    });
91
                } else {
92
                    $error = 'cannot';
93
                }
94
            } else {
95
                if ( $biblio->can_be_recalled({ patron => $patron }) ) {
96
                    ( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall({
97
                        patron => $patron,
98
                        biblio => $biblio,
99
                        branchcode => $pickuploc,
100
                        expirationdate => $expdate,
101
                        interface => 'staff',
102
                    });
103
                } else {
104
                    $error = 'cannot';
105
                }
106
            }
107
            if ( !defined $recall ) {
108
                $error = 'failed';
109
            } else {
110
                # successful recall, go back to Recalls tab
111
                print $input->redirect("/cgi-bin/koha/recalls/request.pl?biblionumber=$biblionumber");
112
            }
113
        }
114
    }
115
}
116
117
if ( $borrowernumber ) {
118
    my $patron = Koha::Patrons->find( $borrowernumber );
119
120
    if ( C4::Context->preference('UseRecalls') and C4::Context->preference('RecallsInterface') ne 'opac' and $patron and $patron->borrowernumber ) {
121
        if ( !$biblio->can_be_recalled({ patron => $patron }) ) {
122
            $error = 1;
123
        }
50
124
51
if ( $op eq 'list' ) {
125
        $template->param(
52
    $recalls = Koha::Recalls->search({ biblio_id => $biblionumber, completed => 0 });
126
            patron => $patron,
53
    $biblio = Koha::Biblios->find( $biblionumber );
127
        );
128
    }
54
}
129
}
55
130
131
my $recalls = Koha::Recalls->search({ biblio_id => $biblionumber, completed => 0 });
132
my $branches = Koha::Libraries->search;
133
my $single_branch_mode = $branches->count == 1;
134
my $items = Koha::Items->search({ biblionumber => $biblionumber });
135
56
$template->param(
136
$template->param(
57
    recalls     => $recalls,
137
    recalls     => $recalls,
58
    recallsview => 1,
138
    recallsview => 1,
59
    biblio      => $biblio,
139
    biblio      => $biblio,
60
    checkboxes  => 1,
140
    checkboxes  => 1,
141
    branches    => $branches,
142
    items       => $items,
143
    error       => $error,
144
    single_branch_mode => $single_branch_mode,
61
    C4::Search::enabled_staff_search_views,
145
    C4::Search::enabled_staff_search_views,
146
    attribute_type_codes => ( C4::Context->preference('ExtendedPatronAttributes')
147
        ? [ Koha::Patron::Attribute::Types->search( { staff_searchable => 1 } )->get_column('code') ]
148
        : []
149
    ),
62
);
150
);
63
151
64
output_html_with_http_headers $input, $cookie, $template->output;
152
output_html_with_http_headers $input, $cookie, $template->output;
(-)a/reserve/modrequest.pl (-1 / +13 lines)
Lines 52-57 my @branch = $query->multi_param('pickup'); Link Here
52
my @itemnumber = $query->multi_param('itemnumber');
52
my @itemnumber = $query->multi_param('itemnumber');
53
my @biblionumber = $query->multi_param('biblionumber');
53
my @biblionumber = $query->multi_param('biblionumber');
54
my $count=@rank;
54
my $count=@rank;
55
my $recallerror;
55
56
56
@biblionumber = uniq @biblionumber;
57
@biblionumber = uniq @biblionumber;
57
58
Lines 86-91 else { Link Here
86
            $params->{reservedate} = $reservedates[$i] || undef;
87
            $params->{reservedate} = $reservedates[$i] || undef;
87
        }
88
        }
88
89
90
        if ( $rank[$i] eq 'recall' and !$recallerror ) {
91
            my $hold = Koha::Holds->find( $reserve_id[$i] );
92
            if ( !$hold->biblio->can_be_recalled({ patron => $hold->patron }) ) {
93
                $recallerror = 1;
94
            }
95
        }
96
89
        try {
97
        try {
90
            ModReserve($params);
98
            ModReserve($params);
91
        } catch {
99
        } catch {
Lines 112-117 if ( $from eq 'borrower'){ Link Here
112
    print $query->redirect("/cgi-bin/koha/circ/circulation.pl?borrowernumber=$borrower[0]");
120
    print $query->redirect("/cgi-bin/koha/circ/circulation.pl?borrowernumber=$borrower[0]");
113
} else {
121
} else {
114
     my $url = URI->new("/cgi-bin/koha/reserve/request.pl");
122
     my $url = URI->new("/cgi-bin/koha/reserve/request.pl");
115
     $url->query_form( biblionumber => [@biblionumber]);
123
     if ( $recallerror ) {
124
         $url->query_form( biblionumber => [@biblionumber], recallerror => 1 );
125
     } else {
126
         $url->query_form( biblionumber => [@biblionumber] );
127
     }
116
     print $query->redirect($url);
128
     print $query->redirect($url);
117
}
129
}
(-)a/reserve/request.pl (+5 lines)
Lines 715-720 $template->param( pickup => $pickup || C4::Context->userenv->{branch} ); Link Here
715
715
716
$template->param(borrowernumber => $borrowernumber_hold);
716
$template->param(borrowernumber => $borrowernumber_hold);
717
717
718
my $recallerror = $input->param('recallerror');
719
if ( $recallerror ) {
720
    $template->param( recallerror => 1 );
721
}
722
718
# printout the page
723
# printout the page
719
output_html_with_http_headers $input, $cookie, $template->output;
724
output_html_with_http_headers $input, $cookie, $template->output;
720
725
(-)a/t/db_dependent/Holds.t (-2 / +80 lines)
Lines 7-13 use t::lib::TestBuilder; Link Here
7
7
8
use C4::Context;
8
use C4::Context;
9
9
10
use Test::More tests => 75;
10
use Test::More tests => 76;
11
use Test::Exception;
11
use Test::Exception;
12
12
13
use MARC::Record;
13
use MARC::Record;
Lines 1801-1803 subtest 'Koha::Holds->get_items_that_can_fill returns items with datecancelled o Link Here
1801
1801
1802
    $schema->storage->txn_rollback;
1802
    $schema->storage->txn_rollback;
1803
};
1803
};
1804
- 
1804
1805
subtest 'ModReserve to convert a hold to a recall' => sub {
1806
    plan tests => 2;
1807
1808
    $schema->storage->txn_begin;
1809
1810
    my $category = $builder->build({ source => 'Category' });
1811
    my $branch = $builder->build({ source => 'Branch' })->{ branchcode };
1812
    my $biblio = $builder->build_sample_biblio( { itemtype => 'DUMMY' } );
1813
    my $item = $builder->build_sample_item(
1814
        { library => $branch, biblionumber => $biblio->biblionumber } );
1815
1816
    my $patron = Koha::Patron->new(
1817
        {
1818
            firstname    => 'my firstname',
1819
            surname      => 'whatever surname',
1820
            categorycode => $category->{categorycode},
1821
            branchcode   => $branch,
1822
        }
1823
    )->store;
1824
    my $borrowernumber = $patron->borrowernumber;
1825
1826
    my $patron2 = Koha::Patron->new(
1827
        {
1828
            firstname    => 'my firstname',
1829
            surname      => 'whatever surname',
1830
            categorycode => $category->{categorycode},
1831
            branchcode   => $branch,
1832
        }
1833
    )->store;
1834
1835
    t::lib::Mocks::mock_preference("UseRecalls", 1);
1836
    t::lib::Mocks::mock_userenv( { branchcode => $branch } );
1837
1838
    C4::Circulation::AddIssue( $patron2->unblessed, $item->barcode );
1839
1840
    Koha::CirculationRules->set_rules({
1841
        branchcode => undef,
1842
        categorycode => undef,
1843
        itemtype => undef,
1844
        rules => {
1845
            recalls_allowed => 5,
1846
            recalls_per_record => 1,
1847
            on_shelf_recalls => 'any',
1848
        },
1849
    });
1850
1851
    my $reserve_id = AddReserve(
1852
        {
1853
            branchcode     => $branch,
1854
            borrowernumber => $borrowernumber,
1855
            biblionumber   => $biblio->biblionumber,
1856
            priority       =>
1857
              C4::Reserves::CalculatePriority( $biblio->biblionumber ),
1858
            itemnumber => $item->id,
1859
        }
1860
    );
1861
1862
    my $hold = Koha::Holds->find($reserve_id);
1863
    my $before_recalls = Koha::Recalls->search->filter_by_current->count;
1864
1865
    ModReserve(
1866
        {
1867
            reserve_id     => $hold->id,
1868
            expirationdate => '1981-06-10',
1869
            priority       => 99,
1870
            rank           => "recall",
1871
            itemnumber     => $item->id,
1872
        }
1873
    );
1874
1875
    $hold = Koha::Holds->find($reserve_id);
1876
    my $after_recalls = Koha::Recalls->search->filter_by_current->count;
1877
1878
    ok( !defined $hold, 'Hold was cancelled' );
1879
    is( $after_recalls, $before_recalls + 1, 'Recall count increased by 1 as hold was converted to a recall' );
1880
1881
    $schema->storage->txn_rollback;
1882
};

Return to bug 31391