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

(-)a/C4/Reserves.pm (+37 lines)
Lines 1092-1097 sub ModReserve { Link Here
1092
1092
1093
    if ( $rank eq "del" ) {
1093
    if ( $rank eq "del" ) {
1094
        $hold->cancel( { cancellation_reason => $cancellation_reason } );
1094
        $hold->cancel( { cancellation_reason => $cancellation_reason } );
1095
    } elsif ( $rank eq "recall" ) {
1096
        if ( !$biblionumber ) {
1097
            $biblionumber = $hold->biblionumber;
1098
        }
1099
        my $biblio = Koha::Biblios->find($biblionumber);
1100
        if ( !$borrowernumber ) {
1101
            $borrowernumber = $hold->borrowernumber;
1102
        }
1103
        my $patron = Koha::Patrons->find($borrowernumber);
1104
        if ( $hold->item_level_hold ) {
1105
            if ( $hold->item->can_be_recalled( { patron => $patron, hold_convert => 1 } ) ) {
1106
                my ( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall(
1107
                    {
1108
                        patron         => $patron,
1109
                        biblio         => $biblio,
1110
                        branchcode     => $branchcode,
1111
                        expirationdate => $date,
1112
                        interface      => 'intranet',
1113
                        item           => $hold->item,
1114
                    }
1115
                );
1116
                $hold->cancel( { cancellation_reason => 'RECALLED' } );
1117
            }
1118
        } else {
1119
            if ( $biblio->can_be_recalled( { patron => $patron, hold_convert => 1 } ) ) {
1120
                my ( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall(
1121
                    {
1122
                        patron         => $patron,
1123
                        biblio         => $biblio,
1124
                        branchcode     => $branchcode,
1125
                        expirationdate => $date,
1126
                        interface      => 'intranet',
1127
                    }
1128
                );
1129
                $hold->cancel( { cancellation_reason => 'RECALLED' } );
1130
            }
1131
        }
1095
    } elsif ( $hold->found && $hold->priority eq '0' && $date ) {
1132
    } elsif ( $hold->found && $hold->priority eq '0' && $date ) {
1096
1133
1097
        # The only column that can be updated for a found hold is the expiration date
1134
        # The only column that can be updated for a found hold is the expiration date
(-)a/Koha/Biblio.pm (-1 / +3 lines)
Lines 2100-2105 sub recalls { Link Here
2100
2100
2101
Does biblio-level checks and returns the items attached to this biblio that are available for recall
2101
Does biblio-level checks and returns the items attached to this biblio that are available for recall
2102
2102
2103
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.
2104
2103
=cut
2105
=cut
2104
2106
2105
sub can_be_recalled {
2107
sub can_be_recalled {
Lines 2125-2131 sub can_be_recalled { Link Here
2125
    my @all_itemnumbers;
2127
    my @all_itemnumbers;
2126
    foreach my $item (@all_items) {
2128
    foreach my $item (@all_items) {
2127
        push( @all_itemnumbers, $item->itemnumber );
2129
        push( @all_itemnumbers, $item->itemnumber );
2128
        if ( $item->can_be_recalled( { patron => $patron } ) ) {
2130
        if ( $item->can_be_recalled( { patron => $patron, hold_convert => $params->{hold_convert} } ) ) {
2129
            push( @itemtypes,   $item->effective_itemtype );
2131
            push( @itemtypes,   $item->effective_itemtype );
2130
            push( @itemnumbers, $item->itemnumber );
2132
            push( @itemnumbers, $item->itemnumber );
2131
            push( @items,       $item );
2133
            push( @items,       $item );
(-)a/Koha/Item.pm (-3 / +8 lines)
Lines 2312-2317 sub recall { Link Here
2312
2312
2313
Does item-level checks and returns if items can be recalled by this borrower
2313
Does item-level checks and returns if items can be recalled by this borrower
2314
2314
2315
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.
2316
2315
=cut
2317
=cut
2316
2318
2317
sub can_be_recalled {
2319
sub can_be_recalled {
Lines 2373-2381 sub can_be_recalled { Link Here
2373
            ->count > 0 );
2375
            ->count > 0 );
2374
2376
2375
        # check if this patron has already reserved this item
2377
        # check if this patron has already reserved this item
2376
        return 0
2378
        unless ( $params->{hold_convert} ) {
2377
            if ( Koha::Holds->search( { itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber } )
2379
            return 0
2378
            ->count > 0 );
2380
                if (
2381
                Koha::Holds->search( { itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber } )
2382
                ->count > 0 );
2383
        }
2379
    }
2384
    }
2380
2385
2381
    # check item availability
2386
    # check item availability
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/holds_table.inc (+6 lines)
Lines 69-74 Link Here
69
                                    <option value="[% hold.priority | html %]" selected="selected">[% this_priority | html %]</option>
69
                                    <option value="[% hold.priority | html %]" selected="selected">[% this_priority | html %]</option>
70
                                [% END %]
70
                                [% END %]
71
                                <option value="del">del</option>
71
                                <option value="del">del</option>
72
                                [% IF Koha.Preference('UseRecalls') AND Koha.Preference('RecallsInterface') != 'opac' %]
73
                                    <option value="recall">recall</option>
74
                                [% END %]
72
                            </select>
75
                            </select>
73
                        [% ELSE %]
76
                        [% ELSE %]
74
                            <input type="hidden" name="rank-request" class="rank-request" value="[% hold.priority | html %]" data-hold-id="[% hold.reserve_id | html %]" />
77
                            <input type="hidden" name="rank-request" class="rank-request" value="[% hold.priority | html %]" data-hold-id="[% hold.reserve_id | html %]" />
Lines 84-89 Link Here
84
                                [% ELSE %]
87
                                [% ELSE %]
85
                                    <option value="[% hold.priority | html %]" selected="selected">[% this_priority | html %]</option>
88
                                    <option value="[% hold.priority | html %]" selected="selected">[% this_priority | html %]</option>
86
                                [% END %]
89
                                [% END %]
90
                                [% IF Koha.Preference('UseRecalls') AND Koha.Preference('RecallsInterface') != 'opac' %]
91
                                    <option value="recall">recall</option>
92
                                [% END %]
87
                            </select>
93
                            </select>
88
                        [% END %]
94
                        [% END %]
89
                    [% ELSE %]
95
                    [% ELSE %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/recalls/request.tt (-19 / +230 lines)
Lines 2-13 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 %]
10
[% SET libraries = Branches.all %]
11
[% SET categories = Categories.all.unblessed %]
12
[% SET columns = ['name', 'cardnumber', 'dateofbirth', 'category', 'branch', 'address', 'phone'] %]
13
[% PROCESS "patron-search.inc" %]
5
[% PROCESS 'i18n.inc' %]
14
[% PROCESS 'i18n.inc' %]
6
[% SET footerjs = 1 %]
15
[% SET footerjs = 1 %]
7
[% INCLUDE 'doc-head-open.inc' %]
16
[% INCLUDE 'doc-head-open.inc' %]
8
<title
17
<title
9
    >[% FILTER collapse %]
18
    >[% FILTER collapse %]
10
        [% t("Existing recalls") | html %]
19
        [% t("Place a recall") | html %]
11
        &rsaquo; [% t("Recalls") | html %] &rsaquo; [% t("Koha") | html %]
20
        &rsaquo; [% t("Recalls") | html %] &rsaquo; [% t("Koha") | html %]
12
    [% END %]</title
21
    [% END %]</title
13
>
22
>
Lines 27-60 Link Here
27
            [% INCLUDE 'biblio-title.inc' link = 1 %]
36
            [% INCLUDE 'biblio-title.inc' link = 1 %]
28
        [% END %]
37
        [% END %]
29
        [% WRAPPER breadcrumb_item bc_active= 1 %]
38
        [% WRAPPER breadcrumb_item bc_active= 1 %]
30
            <span>Existing recalls</span>
39
            <span>Place a recall</span>
31
        [% END %]
40
        [% END %]
32
    [% END #/ WRAPPER breadcrumbs %]
41
    [% END #/ WRAPPER breadcrumbs %]
33
[% END #/ WRAPPER sub-header.inc %]
42
[% END #/ WRAPPER sub-header.inc %]
34
43
35
[% WRAPPER 'main-container.inc' aside='biblio-view-menu' %]
44
[% WRAPPER 'main-container.inc' aside='biblio-view-menu' %]
36
    <h1>Existing recalls</h1>
45
    <h1>Recalls</h1>
37
46
38
    [% IF Koha.Preference('UseRecalls') %]
47
    <h2>Place a recall on [% INCLUDE 'biblio-title.inc' link = 1 %]</h2>
39
        [% IF recalls.count %]
48
40
            <div class="page-section">
49
    [% IF patron %]
41
                <form method="post" action="/cgi-bin/koha/recalls/request.pl">
50
51
        [% IF error %]
52
            <div class="dialog alert"> Unable to place a recall. Check your circulation rules. </div>
53
        [% END %]
54
55
        <fieldset class="rows">
56
            <legend>Recall details</legend>
57
            <form id="recallform" action="/cgi-bin/koha/recalls/request.pl" method="post">
58
                <input type="hidden" name="biblionumber" value="[% biblio.biblionumber | html %]" />
59
60
                <ol>
61
                    <li>
62
                        <label for="borrowernumber">Patron:</label> <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% patron.borrowernumber | uri %]">[% INCLUDE 'patron-title.inc' %]</a>
63
                        <input type="hidden" name="borrowernumber" value="[% patron.borrowernumber | html %]" />
64
                    </li>
65
66
                    [% UNLESS ( single_branch_mode ) %]
67
                        <li>
68
                            <label for="pickup">Pick up at:</label>
69
                            <select name="pickup" id="pickup">
70
                                [% FOREACH branch IN branches %]
71
                                    [% IF branch.branchcode == patron.branchcode %]
72
                                        <option value="[% branch.branchcode | html %]" selected>[% Branches.GetName( branch.branchcode ) | html %]</option>
73
                                    [% ELSE %]
74
                                        <option value="[% branch.branchcode | html %]">[% Branches.GetName( branch.branchcode ) | html %]</option>
75
                                    [% END %]
76
                                [% END %]
77
                            </select>
78
                        </li>
79
                    [% END %]
80
81
                    <li> <label for="expirationdate">Recall not needed after:</label> <input type="text" name="expirationdate" id="expirationdate" size="20" class="flatpickr" /> </li>
82
                    <li> <label for="type">Recall next available item</label> <input type="checkbox" name="type" id="type" checked /> </li>
83
                </ol>
84
85
                <fieldset class="action">
86
                    [% INCLUDE 'csrf-token.inc' %]
87
                    <input type="hidden" name="op" value="cud-request" />
88
                    <input type="hidden" name="biblionumber" value="[% biblio.biblionumber | html %]" />
89
                    [% IF error %]
90
                        <input type="submit" class="btn btn-default" value="Place recall" disabled="disabled" />
91
                    [% ELSE %]
92
                        <input type="submit" class="btn btn-default" value="Place recall" />
93
                    [% END %]
94
                    <a href="/cgi-bin/koha/recalls/request.pl?biblionumber=[% biblio.biblionumber | uri %]" class="cancel">Cancel</a>
95
                </fieldset>
96
97
                <table id="items">
98
                    <caption>Place a recall on a specific item</caption>
99
                    <thead>
100
                        <tr>
101
                            <th>&nbsp;</th>
102
                            <th>Item type</th>
103
                            <th>Barcode</th>
104
                            [% UNLESS ( single_branch_mode ) %]
105
                                <th>Home library</th>
106
                                <th>Last location</th>
107
                            [% END %]
108
                            <th>Collection</th>
109
                            <th>Call number</th>
110
                            <th>Copy number</th>
111
                            <th>Vol no.</th>
112
                            <th>Information</th>
113
                        </tr>
114
                    </thead>
115
                    <tbody>
116
                        [% FOREACH item IN items %]
117
                            <tr>
118
                                <td>
119
                                    [% IF item.can_be_recalled( patron => patron ) %]
120
                                        <input type="radio" class="itemnumber" name="itemnumber" value="[% item.itemnumber | html %]" />
121
                                    [% ELSE %]
122
                                        <span class="error">
123
                                            <i class="fa fa-times fa-lg" title="Cannot be recalled"></i>
124
                                        </span>
125
                                    [% END %]
126
                                </td>
127
                                <td>[% ItemTypes.GetDescription( item.effective_itemtype ) | html %]</td>
128
                                <td>[% item.barcode | html %]</td>
129
                                [% UNLESS ( single_branch_mode ) %]
130
                                    <td>[% Branches.GetName( item.homebranch ) | html %]</td>
131
                                    <td>[% Branches.GetName( item.holdingbranch) | html %]</td>
132
                                [% END %]
133
                                <td>[% AuthorisedValues.GetByCode( 'CCODE', item.ccode, 1 ) | html %]</td>
134
                                <td>[% item.itemcallnumber | html %]</td>
135
                                <td>[% item.copynumber | html %]</td>
136
                                <td>[% item.enumchron | html %]</td>
137
                                <td>
138
                                    [% IF ( item.checkout ) %]
139
                                        <span class="checkedout">Due [% item.checkout.date_due | $KohaDates %]</span>
140
                                    [% ELSIF ( item.get_transfer ) %]
141
                                        <span class="intransit"
142
                                            >In transit from [% Branches.GetName( item.get_transfer.frombranch ) | html %] to [% Branches.GetName( item.get_transfer.tobranch ) | html %] since
143
                                            [% item.get_transfer.datesent | $KohaDates %]</span
144
                                        >
145
                                    [% END %]
146
                                    [% IF ( item.itemlost || item.withdrawn ) %]
147
                                        <span class="lost">Unavailable (lost or missing)</span>
148
                                    [% END %]
149
                                    [% IF ( item.notforloan ) %]
150
                                        <span class="notforloan">Not for loan ([% item.notforloan | html %])</span>
151
                                    [% END %]
152
                                    [% hold = item.current_holds.next %]
153
                                    [% IF ( item.recall ) %]
154
                                        <span class="waiting">
155
                                            [% IF ( item.recall.waiting_date ) %]
156
                                                Waiting for patron at [% Branches.GetName( item.recall.pickup_library_id ) | html %] since [% item.recall.waiting_date | $KohaDates %].
157
                                            [% ELSIF ( item.recall.item_id == item.itemnumber ) %]
158
                                                Recalled by patron expected at [% Branches.GetName( item.recall.pickup_library_id ) | html %] since [% item.recall.created_date | $KohaDates %].
159
                                            [% END %]
160
                                        </span>
161
                                    [% ELSIF ( hold.waitingdate ) %]
162
                                        <span class="waiting"> Waiting for patron at [% Branches.GetName( hold.branchcode ) | html %] since [% hold.waitingdate | $KohaDates %]. </span>
163
                                    [% ELSIF ( hold.borrowernumber == logged_in_user.borrowernumber ) %]
164
                                        <span class="waiting"> Patron has already placed a <a href="/cgi-bin/koha/circ/circulation.pl?borrowernumber=[% patron.borrowernumber | uri %]#reserves">reserve</a> on this item. </span>
165
                                    [% END # / IF ( item.recall or hold ) %]
166
                                </td>
167
                            </tr>
168
                        [% END %]
169
                    </tbody>
170
                </table>
171
172
                <fieldset class="action">
42
                    [% INCLUDE 'csrf-token.inc' %]
173
                    [% INCLUDE 'csrf-token.inc' %]
43
                    <input type="hidden" name="op" value="cud-cancel_multiple_recalls" />
174
                    <input type="hidden" name="op" value="cud-request" />
44
                    <input type="hidden" name="biblionumber" value="[% biblio.biblionumber | html %]" />
175
                    <input type="hidden" name="biblionumber" value="[% biblio.biblionumber | html %]" />
45
                    <input type="checkbox" id="select_all" /> <span id="select_all_text">Select all</span>
176
                    [% IF error %]
46
                    [% INCLUDE 'recalls.inc' %]
177
                        <input type="submit" class="btn btn-default" value="Place recall" disabled="disabled" />
47
                    <fieldset class="action">
178
                    [% ELSE %]
48
                        <button type="submit" id="cancel_selected" class="btn btn-default btn-sm">Cancel selected recalls</button>
179
                        <input type="submit" class="btn btn-default" value="Place recall" />
49
                    </fieldset>
180
                    [% END %]
50
                </form>
181
                    <a href="/cgi-bin/koha/recalls/request.pl?biblionumber=[% biblio.biblionumber | uri %]" class="cancel">Cancel</a>
51
            </div>
182
                </fieldset>
52
            <!-- /.page-section -->
183
            </form>
184
        </fieldset>
185
    [% ELSE %]
186
187
        [% IF Koha.Preference('RecallsInterface') == 'opac' %]
188
            <div class="dialog alert"> 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. </div>
53
        [% ELSE %]
189
        [% ELSE %]
54
            <div class="alert alert-info">No recalls have been made.</div>
190
            <fieldset>
191
                <h2>Search patrons</h2>
192
                <div id="circ_recalls_select" class="toptabs">
193
                    <ul class="nav nav-tabs" role="tablist">
194
                        <li role="presentation" class="active"><a href="#recalls_patronsearch_pane" aria-controls="recalls_patronsearch_pane" role="tab" data-toggle="tab">Patrons</a></li>
195
                    </ul>
196
                    <div class="tab-content">
197
                        <div id="recalls_patronsearch_pane" role="tabpanel" class="tab-pane active">
198
                            [% PROCESS patron_search_filters_simple %]
199
                            [% PROCESS patron_search_table table_id => 'table_borrowers', open_on_row_click => 1 %]
200
                        </div>
201
                    </div>
202
                    <!-- /.tab-content -->
203
                </div>
204
            </fieldset>
205
        [% END %]
206
207
        <h2>Existing recalls</h2>
208
209
        [% IF Koha.Preference('UseRecalls') %]
210
            [% IF recalls.count %]
211
                <div class="page-section">
212
                    <form method="post" action="/cgi-bin/koha/recalls/request.pl">
213
                        [% INCLUDE 'csrf-token.inc' %]
214
                        <input type="hidden" name="op" value="cud-cancel_multiple_recalls" />
215
                        <input type="hidden" name="biblionumber" value="[% biblio.biblionumber | html %]" />
216
                        <input type="checkbox" id="select_all" /> <span id="select_all_text">Select all</span>
217
                        [% INCLUDE 'recalls.inc' %]
218
                        <fieldset class="action">
219
                            <button type="submit" id="cancel_selected" class="btn btn-default btn-sm">Cancel selected recalls</button>
220
                        </fieldset>
221
                    </form>
222
                </div>
223
                <!-- /.page-section -->
224
            [% ELSE %]
225
                <div class="alert alert-info">No recalls have been made.</div>
226
            [% END %]
227
        [% ELSE %]
228
            <div class="alert alert-info">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>
55
        [% END %]
229
        [% END %]
56
    [% ELSE %]
57
        <div class="alert alert-info">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>
58
    [% END %]
230
    [% END %]
59
[% END %]
231
[% END %]
60
232
Lines 62-67 Link Here
62
    [% Asset.js("js/recalls.js") | $raw %]
234
    [% Asset.js("js/recalls.js") | $raw %]
63
    [% INCLUDE 'datatables.inc' %]
235
    [% INCLUDE 'datatables.inc' %]
64
    [% INCLUDE 'columns_settings.inc' %]
236
    [% INCLUDE 'columns_settings.inc' %]
237
    [% INCLUDE 'calendar.inc' %]
238
    [% SET url_biblio_params = "biblionumber=" _ biblio.biblionumber %]
239
    [% 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' %]
240
    <script>
241
        table_settings = [% TablesSettings.GetColumns( 'circ', 'circulation', 'table_borrowers', 'json' ) | $raw %];
242
        $(document).ready(function(){
243
            $("#recalls_patronsearch").on("submit", filter);
244
            [% UNLESS patron %]
245
                [% IF ( PatronAutoComplete ) %]
246
                    patron_autocomplete($("#search_patron_filter"), { 'link-to': 'recall', 'url-params': '[% url_biblio_params | url %]' });
247
                [% END %]
248
            [% END %]
249
            $("#items").dataTable($.extend(true, {}, dataTablesDefaults, {
250
                'bPaginate': false,
251
                "sDom": '<"top pager"ilf>t',
252
            }));
253
254
            //Override fieldset styling for dataTables search box
255
            $("div.top.pager").css("margin-left","1em");
256
            $(".dataTables_filter label").css({
257
                "width":"auto",
258
                "margin-right":"0em"
259
            });
260
            $("#type").click(function() {
261
                if(this.checked){
262
                    $("input[name=itemnumber]").each(function() {
263
                        $(this).prop("checked", false);
264
                    });
265
                }
266
            });
267
            $("input[name=itemnumber]").click(function() {
268
                $("input[name=itemnumber]").each(function() {
269
                    if(this.checked){
270
                        $("#type").prop("checked", false);
271
                    }
272
                });
273
            });
274
        });
275
    </script>
65
[% END %]
276
[% END %]
66
277
67
[% INCLUDE 'intranet-bottom.inc' %]
278
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt (+8 lines)
Lines 251-256 Link Here
251
                <p>No club with this name, please, try another</p>
251
                <p>No club with this name, please, try another</p>
252
            </div>
252
            </div>
253
        [% END %]
253
        [% END %]
254
255
        [% IF ( recallerror ) %]
256
            <div class="dialog alert">
257
                <h3>Hold cannot be converted to a recall</h3>
258
                <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>
259
            </div>
260
        [% END %]
261
254
        <fieldset>
262
        <fieldset>
255
            [% UNLESS multi_hold %]
263
            [% UNLESS multi_hold %]
256
                [% IF clubcount %]
264
                [% IF clubcount %]
(-)a/koha-tmpl/intranet-tmpl/prog/js/staff-global.js (-1 / +3 lines)
Lines 719-725 function patron_autocomplete(node, options) { Link Here
719
                    ? "/cgi-bin/koha/circ/circulation.pl"
719
                    ? "/cgi-bin/koha/circ/circulation.pl"
720
                    : link_to == "reserve"
720
                    : link_to == "reserve"
721
                      ? "/cgi-bin/koha/reserve/request.pl"
721
                      ? "/cgi-bin/koha/reserve/request.pl"
722
                      : "/cgi-bin/koha/members/moremember.pl";
722
                      : link_to == "recall"
723
                        ? "/cgi-bin/koha/recalls/request.pl"
724
                        : "/cgi-bin/koha/members/moremember.pl";
723
            item.link +=
725
            item.link +=
724
                (url_params ? "?" + url_params + "&" : "?") +
726
                (url_params ? "?" + url_params + "&" : "?") +
725
                "borrowernumber=" +
727
                "borrowernumber=" +
(-)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 'cud-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 123-132 if ( C4::Context->preference('UseRecalls') and C4::Context->preference('RecallsI Link Here
123
                $error = 'failed';
129
                $error = 'failed';
124
            }
130
            }
125
        }
131
        }
126
    } elsif ( $op eq 'cud-cancel' ) {
127
        my $recall_id = $query->param('recall_id');
128
        Koha::Recalls->find($recall_id)->set_cancelled;
129
        print $query->redirect('/cgi-bin/koha/opac-user.pl');
130
    }
132
    }
131
133
132
    my $branches           = Koha::Libraries->search();
134
    my $branches           = Koha::Libraries->search();
(-)a/recalls/request.pl (-13 / +118 lines)
Lines 35-64 my ( $template, $loggedinuser, $cookie ) = get_template_and_user( Link Here
35
    }
35
    }
36
);
36
);
37
37
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 } );
41
my $biblio         = Koha::Biblios->find($biblionumber);
42
my $biblio       = Koha::Biblios->find($biblionumber);
42
my $borrowernumber = $input->param('borrowernumber');
43
my $error          = $input->param('error');
43
44
44
if ( $op eq 'cud-cancel_multiple_recalls' ) {
45
if ( $op eq 'cud-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
} elsif ( $op eq 'cud-request' ) {
50
51
    if (    C4::Context->preference('UseRecalls')
52
        and C4::Context->preference('RecallsInterface') ne 'opac'
53
        and $borrowernumber )
54
    {
55
        my $patron = Koha::Patrons->find($borrowernumber);
56
57
        unless ( $biblio->can_be_recalled( { patron => $patron } ) ) { $error = 'unavailable'; }
58
        my $items = Koha::Items->search( { biblionumber => $biblionumber } )->as_list;
59
60
        # check if already recalled
61
        my $recalled = $biblio->recalls->filter_by_current->search( { patron_id => $borrowernumber } )->count;
62
        if ( defined $recalled and $recalled > 0 ) {
63
            my $recalls_per_record = Koha::CirculationRules->get_effective_rule(
64
                {
65
                    categorycode => $patron->categorycode,
66
                    branchcode   => undef,
67
                    itemtype     => undef,
68
                    rule_name    => 'recalls_per_record'
69
                }
70
            );
71
            if (    defined $recalls_per_record
72
                and $recalls_per_record->rule_value
73
                and $recalled >= $recalls_per_record->rule_value )
74
            {
75
                $error = 'duplicate';
76
            }
77
        }
78
79
        if ( !defined $error ) {
80
            my $pickuploc  = $input->param('pickup');
81
            my $expdate    = $input->param('expirationdate');
82
            my $level      = $input->param('type');
83
            my $itemnumber = $input->param('itemnumber');
84
85
            my ( $recall, $due_interval, $due_date );
86
87
            if ( !defined $level and defined $itemnumber ) {
88
                my $item = Koha::Items->find($itemnumber);
89
                if ( $item->can_be_recalled( { patron => $patron } ) ) {
90
                    ( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall(
91
                        {
92
                            patron         => $patron,
93
                            biblio         => $biblio,
94
                            branchcode     => $pickuploc,
95
                            item           => $item,
96
                            expirationdate => $expdate,
97
                            interface      => 'staff',
98
                        }
99
                    );
100
                } else {
101
                    $error = 'cannot';
102
                }
103
            } else {
104
                if ( $biblio->can_be_recalled( { patron => $patron } ) ) {
105
                    ( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall(
106
                        {
107
                            patron         => $patron,
108
                            biblio         => $biblio,
109
                            branchcode     => $pickuploc,
110
                            expirationdate => $expdate,
111
                            interface      => 'staff',
112
                        }
113
                    );
114
                } else {
115
                    $error = 'cannot';
116
                }
117
            }
118
            if ( !defined $recall ) {
119
                $error = 'failed';
120
            } else {
121
122
                # successful recall, go back to Recalls tab
123
                print $input->redirect("/cgi-bin/koha/recalls/request.pl?biblionumber=$biblionumber");
124
            }
125
        }
126
    }
49
}
127
}
50
128
51
if ( $op eq 'list' ) {
129
if ($borrowernumber) {
52
    $recalls = Koha::Recalls->search( { biblio_id => $biblionumber, completed => 0 } );
130
    my $patron = Koha::Patrons->find($borrowernumber);
53
    $biblio  = Koha::Biblios->find($biblionumber);
131
132
    if (    C4::Context->preference('UseRecalls')
133
        and C4::Context->preference('RecallsInterface') ne 'opac'
134
        and $patron
135
        and $patron->borrowernumber )
136
    {
137
        if ( !$biblio->can_be_recalled( { patron => $patron } ) ) {
138
            $error = 1;
139
        }
140
141
        $template->param(
142
            patron => $patron,
143
        );
144
    }
54
}
145
}
55
146
147
my $recalls            = Koha::Recalls->search( { biblio_id => $biblionumber, completed => 0 } );
148
my $branches           = Koha::Libraries->search;
149
my $single_branch_mode = $branches->count == 1;
150
my $items              = Koha::Items->search( { biblionumber => $biblionumber } );
151
56
$template->param(
152
$template->param(
57
    recalls     => $recalls,
153
    recalls            => $recalls,
58
    recallsview => 1,
154
    recallsview        => 1,
59
    biblio      => $biblio,
155
    biblio             => $biblio,
60
    checkboxes  => 1,
156
    checkboxes         => 1,
157
    branches           => $branches,
158
    items              => $items,
159
    error              => $error,
160
    single_branch_mode => $single_branch_mode,
61
    C4::Search::enabled_staff_search_views,
161
    C4::Search::enabled_staff_search_views,
162
    attribute_type_codes => (
163
        C4::Context->preference('ExtendedPatronAttributes')
164
        ? [ Koha::Patron::Attribute::Types->search( { staff_searchable => 1 } )->get_column('code') ]
165
        : []
166
    ),
62
);
167
);
63
168
64
output_html_with_http_headers $input, $cookie, $template->output;
169
output_html_with_http_headers $input, $cookie, $template->output;
(-)a/reserve/modrequest.pl (-1 / +13 lines)
Lines 46-51 my @branch = $query->multi_param('pickup'); Link Here
46
my @itemnumber      = $query->multi_param('itemnumber');
46
my @itemnumber      = $query->multi_param('itemnumber');
47
my @biblionumber    = $query->multi_param('biblionumber');
47
my @biblionumber    = $query->multi_param('biblionumber');
48
my $count           = @rank;
48
my $count           = @rank;
49
my $recallerror;
49
50
50
@biblionumber = uniq @biblionumber;
51
@biblionumber = uniq @biblionumber;
51
52
Lines 68-73 if ( $op eq 'cud-cancelall' || $op eq 'cud-modifyall' ) { Link Here
68
            $params->{reservedate} = $reservedates[$i] || undef;
69
            $params->{reservedate} = $reservedates[$i] || undef;
69
        }
70
        }
70
71
72
        if ( $rank[$i] eq 'recall' and !$recallerror ) {
73
            my $hold = Koha::Holds->find( $reserve_id[$i] );
74
            if ( !$hold->biblio->can_be_recalled( { patron => $hold->patron } ) ) {
75
                $recallerror = 1;
76
            }
77
        }
78
71
        try {
79
        try {
72
            ModReserve($params);
80
            ModReserve($params);
73
        } catch {
81
        } catch {
Lines 105-110 if ( $from eq 'borrower' ) { Link Here
105
    print $query->redirect("/cgi-bin/koha/circ/circulation.pl?borrowernumber=$borrower[0]");
113
    print $query->redirect("/cgi-bin/koha/circ/circulation.pl?borrowernumber=$borrower[0]");
106
} else {
114
} else {
107
    my $url = URI->new("/cgi-bin/koha/reserve/request.pl");
115
    my $url = URI->new("/cgi-bin/koha/reserve/request.pl");
108
    $url->query_form( biblionumber => [@biblionumber] );
116
    if ($recallerror) {
117
        $url->query_form( biblionumber => [@biblionumber], recallerror => 1 );
118
    } else {
119
        $url->query_form( biblionumber => [@biblionumber] );
120
    }
109
    print $query->redirect($url);
121
    print $query->redirect($url);
110
}
122
}
(-)a/reserve/request.pl (+5 lines)
Lines 740-745 $template->param( borrowernumber => $borrowernumber_hold ); Link Here
740
740
741
$template->param( failed_holds => \@failed_holds );
741
$template->param( failed_holds => \@failed_holds );
742
742
743
my $recallerror = $input->param('recallerror');
744
if ($recallerror) {
745
    $template->param( recallerror => 1 );
746
}
747
743
# printout the page
748
# printout the page
744
output_html_with_http_headers $input, $cookie, $template->output;
749
output_html_with_http_headers $input, $cookie, $template->output;
745
750
(-)a/t/db_dependent/Holds.t (-1 / +79 lines)
Lines 2108-2110 subtest 'EmailPatronWhenHoldIsPlaced tests' => sub { Link Here
2108
2108
2109
    $schema->storage->txn_rollback;
2109
    $schema->storage->txn_rollback;
2110
};
2110
};
2111
- 
2111
2112
subtest 'ModReserve to convert a hold to a recall' => sub {
2113
    plan tests => 2;
2114
2115
    $schema->storage->txn_begin;
2116
2117
    my $category = $builder->build( { source => 'Category' } );
2118
    my $branch   = $builder->build( { source => 'Branch' } )->{branchcode};
2119
    my $biblio   = $builder->build_sample_biblio( { itemtype => 'DUMMY' } );
2120
    my $item     = $builder->build_sample_item( { library => $branch, biblionumber => $biblio->biblionumber } );
2121
2122
    my $patron = Koha::Patron->new(
2123
        {
2124
            firstname    => 'my firstname',
2125
            surname      => 'whatever surname',
2126
            categorycode => $category->{categorycode},
2127
            branchcode   => $branch,
2128
        }
2129
    )->store;
2130
    my $borrowernumber = $patron->borrowernumber;
2131
2132
    my $patron2 = Koha::Patron->new(
2133
        {
2134
            firstname    => 'my firstname',
2135
            surname      => 'whatever surname',
2136
            categorycode => $category->{categorycode},
2137
            branchcode   => $branch,
2138
        }
2139
    )->store;
2140
2141
    t::lib::Mocks::mock_preference( "UseRecalls", 1 );
2142
    t::lib::Mocks::mock_userenv( { branchcode => $branch } );
2143
2144
    C4::Circulation::AddIssue( $patron2->unblessed, $item->barcode );
2145
2146
    Koha::CirculationRules->set_rules(
2147
        {
2148
            branchcode   => undef,
2149
            categorycode => undef,
2150
            itemtype     => undef,
2151
            rules        => {
2152
                recalls_allowed    => 5,
2153
                recalls_per_record => 1,
2154
                on_shelf_recalls   => 'any',
2155
            },
2156
        }
2157
    );
2158
2159
    my $reserve_id = AddReserve(
2160
        {
2161
            branchcode     => $branch,
2162
            borrowernumber => $borrowernumber,
2163
            biblionumber   => $biblio->biblionumber,
2164
            priority       => C4::Reserves::CalculatePriority( $biblio->biblionumber ),
2165
            itemnumber     => $item->id,
2166
        }
2167
    );
2168
2169
    my $hold           = Koha::Holds->find($reserve_id);
2170
    my $before_recalls = Koha::Recalls->search->filter_by_current->count;
2171
2172
    ModReserve(
2173
        {
2174
            reserve_id     => $hold->id,
2175
            expirationdate => '1981-06-10',
2176
            priority       => 99,
2177
            rank           => "recall",
2178
            itemnumber     => $item->id,
2179
        }
2180
    );
2181
2182
    $hold = Koha::Holds->find($reserve_id);
2183
    my $after_recalls = Koha::Recalls->search->filter_by_current->count;
2184
2185
    ok( !defined $hold, 'Hold was cancelled' );
2186
    is( $after_recalls, $before_recalls + 1, 'Recall count increased by 1 as hold was converted to a recall' );
2187
2188
    $schema->storage->txn_rollback;
2189
};

Return to bug 31391