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

(-)a/Koha/ArticleRequest.pm (-4 / +19 lines)
Lines 83-91 sub complete { Link Here
83
=cut
83
=cut
84
84
85
sub cancel {
85
sub cancel {
86
    my ( $self, $notes ) = @_;
86
    my ( $self, $cancellation_reason, $notes ) = @_;
87
87
88
    $self->status(Koha::ArticleRequest::Status::Canceled);
88
    $self->status(Koha::ArticleRequest::Status::Canceled);
89
    $self->cancellation_reason($cancellation_reason) if $cancellation_reason;
89
    $self->notes($notes) if $notes;
90
    $self->notes($notes) if $notes;
90
    $self->store();
91
    $self->store();
91
    $self->notify();
92
    $self->notify();
Lines 100-111 sub notify { Link Here
100
    my ($self) = @_;
101
    my ($self) = @_;
101
102
102
    my $status = $self->status;
103
    my $status = $self->status;
104
    my $reason = $self->notes;
105
    if ( !defined $reason && $self->cancellation_reason ) {
106
        my $av = Koha::AuthorisedValues->search(
107
            {
108
                category            => 'AR_CANCELLATION',
109
                authorised_value    => $self->cancellation_reason
110
            }
111
        )->next;
112
        $reason = $av->lib_opac ? $av->lib_opac : $av->lib if $av;
113
    }
103
114
104
    require C4::Letters;
115
    require C4::Letters;
105
    if (
116
    if (
106
        my $letter = C4::Letters::GetPreparedLetter(
117
        my $letter = C4::Letters::GetPreparedLetter(
107
            module                 => 'circulation',
118
            module      => 'circulation',
108
            letter_code            => "AR_$status", # AR_PENDING, AR_PROCESSING, AR_COMPLETED, AR_CANCELED
119
            letter_code => "AR_$status"
120
            ,    # AR_PENDING, AR_PROCESSING, AR_COMPLETED, AR_CANCELED
109
            message_transport_type => 'email',
121
            message_transport_type => 'email',
110
            lang                   => $self->borrower->lang,
122
            lang                   => $self->borrower->lang,
111
            tables                 => {
123
            tables                 => {
Lines 116-121 sub notify { Link Here
116
                items            => $self->itemnumber,
128
                items            => $self->itemnumber,
117
                branches         => $self->branchcode,
129
                branches         => $self->branchcode,
118
            },
130
            },
131
            substitute => {
132
                reason => $reason,
133
            },
119
        )
134
        )
120
      )
135
      )
121
    {
136
    {
Lines 125-131 sub notify { Link Here
125
                borrowernumber         => $self->borrowernumber,
140
                borrowernumber         => $self->borrowernumber,
126
                message_transport_type => 'email',
141
                message_transport_type => 'email',
127
            }
142
            }
128
        ) or warn "can't enqueue letter ". $letter->{code};
143
        ) or warn "can't enqueue letter " . $letter->{code};
129
    }
144
    }
130
}
145
}
131
146
(-)a/Koha/Exceptions/ArticleRequests.pm (+17 lines)
Line 0 Link Here
1
package Koha::Exceptions::ArticleRequests;
2
3
use Modern::Perl;
4
5
use Exception::Class (
6
7
    'Koha::Exceptions::ArticleRequests' => {
8
        description => 'Something went wrong!',
9
    },
10
    'Koha::Exceptions::ArticleRequests::FailedCancel' => {
11
        isa => 'Koha::Exceptions::ArticleRequests',
12
        description => 'Failed to cancel article request'
13
    }
14
15
);
16
17
1;
(-)a/Koha/REST/V1/ArticleRequests.pm (+76 lines)
Line 0 Link Here
1
package Koha::REST::V1::ArticleRequests;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use Mojo::Base 'Mojolicious::Controller';
21
22
use Koha::Database;
23
use Koha::ArticleRequests;
24
25
use Scalar::Util qw( blessed );
26
use Try::Tiny qw( catch try );
27
28
=head1 NAME
29
30
Koha::REST::V1::ArticleRequests
31
32
=head1 API
33
34
=head2 Methods
35
36
=head3 cancel
37
38
Controller function that handles cancelling a Koha::ArticleRequest object
39
40
=cut
41
42
sub cancel {
43
    my $c = shift->openapi->valid_input or return;
44
45
    my $ar = Koha::ArticleRequests->find( $c->validation->param('ar_id') );
46
47
    unless ( $ar ) {
48
        return $c->render(
49
            status  => 404,
50
            openapi => { error => "Article request not found" }
51
        );
52
    }
53
54
    my $reason = $c->validation->param('cancellation_reason');
55
    my $notes = $c->validation->param('notes');
56
57
    return try {
58
59
        $ar->cancel($reason, $notes);
60
        return $c->render(
61
            status  => 204,
62
            openapi => q{}
63
        );
64
    } catch {
65
        if ( blessed $_ && $_->isa('Koha::Exceptions::ArticleRequests::FailedCancel') ) {
66
            return $c->render(
67
                status  => 403,
68
                openapi => { error => "Article request cannot be canceled" }
69
            );
70
        }
71
72
        $c->unhandled_exception($_);
73
    };
74
}
75
76
1;
(-)a/Koha/REST/V1/Patrons.pm (+49 lines)
Lines 419-422 sub guarantors_can_see_checkouts { Link Here
419
    };
419
    };
420
}
420
}
421
421
422
=head3 cancel_article_request
423
424
Controller function that handles cancelling a patron's Koha::ArticleRequest object
425
426
=cut
427
428
sub cancel_article_request {
429
    my $c = shift->openapi->valid_input or return;
430
431
    my $patron = Koha::Patrons->find( $c->validation->param('patron_id') );
432
433
    unless ( $patron ) {
434
        return $c->render(
435
            status  => 404,
436
            openapi => { error => "Patron not found" }
437
        );
438
    }
439
440
    my $ar = $patron->article_requests->find( $c->validation->param('ar_id') );
441
442
    unless ( $ar ) {
443
        return $c->render(
444
            status  => 404,
445
            openapi => { error => "Article request not found" }
446
        );
447
    }
448
449
    my $reason = $c->validation->param('cancellation_reason');
450
    my $notes = $c->validation->param('notes');
451
452
    return try {
453
454
        $ar->cancel($reason, $notes);
455
        return $c->render(
456
            status  => 204,
457
            openapi => q{}
458
        );
459
    } catch {
460
        if ( blessed $_ && $_->isa('Koha::Exceptions::ArticleRequests::FailedCancel') ) {
461
            return $c->render(
462
                status  => 403,
463
                openapi => { error => "Article request cannot be canceled" }
464
            );
465
        }
466
467
        $c->unhandled_exception($_);
468
    };
469
}
470
422
1;
471
1;
(-)a/api/v1/swagger/parameters.json (+9 lines)
Lines 56-61 Link Here
56
  "cashup_id_pp": {
56
  "cashup_id_pp": {
57
    "$ref": "parameters/cashup.json#/cashup_id_pp"
57
    "$ref": "parameters/cashup.json#/cashup_id_pp"
58
  },
58
  },
59
  "ar_id_pp": {
60
    "$ref": "parameters/article_request.json#/ar_id_pp"
61
  },
62
  "ar_reason_qp": {
63
    "$ref": "parameters/article_request.json#/ar_reason_qp"
64
  },
65
  "ar_notes_qp": {
66
    "$ref": "parameters/article_request.json#/ar_notes_qp"
67
  },
59
  "match": {
68
  "match": {
60
    "name": "_match",
69
    "name": "_match",
61
    "in": "query",
70
    "in": "query",
(-)a/api/v1/swagger/parameters/article_request.json (+23 lines)
Line 0 Link Here
1
{
2
  "ar_id_pp": {
3
    "name": "ar_id",
4
    "in": "path",
5
    "description": "Article request identifier",
6
    "required": true,
7
    "type": "integer"
8
  },
9
  "ar_reason_qp": {
10
    "name": "cancellation_reason",
11
    "in": "query",
12
    "description": "Article request cancellation reason",
13
    "required": false,
14
    "type": "string"
15
  },
16
  "ar_notes_qp": {
17
    "name": "notes",
18
    "in": "query",
19
    "description": "Article request custom cancellation reason",
20
    "required": false,
21
    "type": "string"
22
  }
23
}
(-)a/api/v1/swagger/paths.json (-1 / +7 lines)
Lines 17-22 Link Here
17
  "/acquisitions/funds": {
17
  "/acquisitions/funds": {
18
    "$ref": "paths/acquisitions_funds.json#/~1acquisitions~1funds"
18
    "$ref": "paths/acquisitions_funds.json#/~1acquisitions~1funds"
19
  },
19
  },
20
  "/article_requests/{ar_id}": {
21
    "$ref": "paths/article_requests.json#/~1article_requests~1{ar_id}"
22
  },
20
  "/biblios/{biblio_id}": {
23
  "/biblios/{biblio_id}": {
21
    "$ref": "paths/biblios.json#/~1biblios~1{biblio_id}"
24
    "$ref": "paths/biblios.json#/~1biblios~1{biblio_id}"
22
  },
25
  },
Lines 134-140 Link Here
134
  "/patrons/{patron_id}/extended_attributes/{extended_attribute_id}": {
137
  "/patrons/{patron_id}/extended_attributes/{extended_attribute_id}": {
135
    "$ref": "paths/patrons_extended_attributes.json#/~1patrons~1{patron_id}~1extended_attributes~1{extended_attribute_id}"
138
    "$ref": "paths/patrons_extended_attributes.json#/~1patrons~1{patron_id}~1extended_attributes~1{extended_attribute_id}"
136
  },
139
  },
137
   "/patrons/{patron_id}/holds": {
140
  "/patrons/{patron_id}/holds": {
138
    "$ref": "paths/patrons_holds.json#/~1patrons~1{patron_id}~1holds"
141
    "$ref": "paths/patrons_holds.json#/~1patrons~1{patron_id}~1holds"
139
  },
142
  },
140
  "/patrons/{patron_id}/password": {
143
  "/patrons/{patron_id}/password": {
Lines 170-175 Link Here
170
  "/public/patrons/{patron_id}/guarantors/can_see_checkouts": {
173
  "/public/patrons/{patron_id}/guarantors/can_see_checkouts": {
171
    "$ref": "paths/public_patrons.json#/~1public~1patrons~1{patron_id}~1guarantors~1can_see_checkouts"
174
    "$ref": "paths/public_patrons.json#/~1public~1patrons~1{patron_id}~1guarantors~1can_see_checkouts"
172
  },
175
  },
176
  "/public/patrons/{patron_id}/article_requests/{ar_id}": {
177
    "$ref": "paths/public_patrons.json#/~1public~1patrons~1{patron_id}~1article_requests~1{ar_id}"
178
  },
173
  "/quotes": {
179
  "/quotes": {
174
    "$ref": "paths/quotes.json#/~1quotes"
180
    "$ref": "paths/quotes.json#/~1quotes"
175
  },
181
  },
(-)a/api/v1/swagger/paths/article_requests.json (+70 lines)
Line 0 Link Here
1
{
2
    "/article_requests/{ar_id}": {
3
        "delete": {
4
            "x-mojo-to": "ArticleRequests#cancel",
5
            "operationId": "cancelArticleRequest",
6
            "tags": [
7
                "article_requests"
8
            ],
9
            "summary": "Cancel article requests",
10
            "parameters": [
11
                {
12
                    "$ref": "../parameters.json#/ar_id_pp"
13
                },
14
                {
15
                    "$ref": "../parameters.json#/ar_reason_qp"
16
                },
17
                {
18
                    "$ref": "../parameters.json#/ar_notes_qp"
19
                }
20
            ],
21
            "produces": ["application/json"],
22
            "responses": {
23
                "204": {
24
                    "description": "Article request canceled"
25
                },
26
                "400": {
27
                    "description": "Bad request",
28
                    "schema": {
29
                        "$ref": "../definitions.json#/error"
30
                    }
31
                },
32
                "401": {
33
                    "description": "Authentication required",
34
                    "schema": {
35
                        "$ref": "../definitions.json#/error"
36
                    }
37
                },
38
                "403": {
39
                    "description": "Access forbidden",
40
                    "schema": {
41
                        "$ref": "../definitions.json#/error"
42
                    }
43
                },
44
                "404": {
45
                    "description": "Patron not found",
46
                    "schema": {
47
                        "$ref": "../definitions.json#/error"
48
                    }
49
                },
50
                "500": {
51
                    "description": "Internal server error",
52
                    "schema": {
53
                        "$ref": "../definitions.json#/error"
54
                    }
55
                },
56
                "503": {
57
                    "description": "Under maintenance",
58
                    "schema": {
59
                        "$ref": "../definitions.json#/error"
60
                    }
61
                }
62
            },
63
            "x-koha-authorization": {
64
                "permissions": {
65
                    "reserveforothers": "1"
66
                  }
67
            }
68
        }
69
    }
70
}
(-)a/api/v1/swagger/paths/public_patrons.json (+70 lines)
Lines 242-246 Link Here
242
                "allow-owner": true
242
                "allow-owner": true
243
            }
243
            }
244
        }
244
        }
245
    },
246
    "/public/patrons/{patron_id}/article_requests/{ar_id}": {
247
        "delete": {
248
            "x-mojo-to": "Patrons#cancel_article_request",
249
            "operationId": "cancelPatronArticleRequest",
250
            "tags": [
251
                "patrons",
252
                "article_requests"
253
            ],
254
            "summary": "Cancel patron's article requests",
255
            "parameters": [
256
                {
257
                    "$ref": "../parameters.json#/patron_id_pp"
258
                },
259
                {
260
                    "$ref": "../parameters.json#/ar_id_pp"
261
                },
262
                {
263
                    "$ref": "../parameters.json#/ar_reason_qp"
264
                },
265
                {
266
                    "$ref": "../parameters.json#/ar_notes_qp"
267
                }
268
            ],
269
            "produces": ["application/json"],
270
            "responses": {
271
                "204": {
272
                    "description": "Patron's article request canceled"
273
                },
274
                "400": {
275
                    "description": "Bad request",
276
                    "schema": {
277
                        "$ref": "../definitions.json#/error"
278
                    }
279
                },
280
                "401": {
281
                    "description": "Authentication required",
282
                    "schema": {
283
                        "$ref": "../definitions.json#/error"
284
                    }
285
                },
286
                "403": {
287
                    "description": "Access forbidden",
288
                    "schema": {
289
                        "$ref": "../definitions.json#/error"
290
                    }
291
                },
292
                "404": {
293
                    "description": "Patron not found",
294
                    "schema": {
295
                        "$ref": "../definitions.json#/error"
296
                    }
297
                },
298
                "500": {
299
                    "description": "Internal server error",
300
                    "schema": {
301
                        "$ref": "../definitions.json#/error"
302
                    }
303
                },
304
                "503": {
305
                    "description": "Under maintenance",
306
                    "schema": {
307
                        "$ref": "../definitions.json#/error"
308
                    }
309
                }
310
            },
311
            "x-koha-authorization": {
312
                "allow-owner": true
313
            }
314
        }
245
    }
315
    }
246
}
316
}
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/authorised_values.tt (+2 lines)
Lines 424-429 Authorized values &rsaquo; Administration &rsaquo; Koha Link Here
424
            <p>General holdings: type of unit designator</p>
424
            <p>General holdings: type of unit designator</p>
425
        [% CASE 'HOLD_CANCELLATION' %]
425
        [% CASE 'HOLD_CANCELLATION' %]
426
            <p>Reasons why a hold might have been cancelled</p>
426
            <p>Reasons why a hold might have been cancelled</p>
427
        [% CASE 'AR_CANCELLATION' %]
428
            <p>Reasons why an article request might have been cancelled</p>
427
        [% CASE 'HSBND_FREQ' %]
429
        [% CASE 'HSBND_FREQ' %]
428
            <p>Frequencies used by the housebound module. They are displayed on the housebound tab in the patron account in staff.</p>
430
            <p>Frequencies used by the housebound module. They are displayed on the housebound tab in the patron account in staff.</p>
429
        [% CASE 'ITEMTYPECAT' %]
431
        [% CASE 'ITEMTYPECAT' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/article-requests.tt (-23 / +76 lines)
Lines 36-42 Link Here
36
                    Complete request
36
                    Complete request
37
                </a>
37
                </a>
38
38
39
                <a class="ar-cancel-request" href="#" onclick="HandleMulti( Cancel, [% id_arg | html %], $(this) ); return false;">
39
                <a class="ar-cancel-request" href="#" onclick="Cancel( [% id_arg | html %], $(this) ); return false;">
40
                    <i class="fa fa-minus-circle"></i>
40
                    <i class="fa fa-minus-circle"></i>
41
                    Cancel request
41
                    Cancel request
42
                </a>
42
                </a>
Lines 75-80 Link Here
75
      </div>
75
      </div>
76
    </div>
76
    </div>
77
[% END %]
77
[% END %]
78
[% BLOCK cancel_modal %]
79
    <div id="cancelModal" class="modal" tabindex="-1" role="dialog" aria-hidden="true">
80
        <div class="modal-dialog" role="document">
81
            <div class="modal-content">
82
                <div class="modal-header">
83
                    <button type="button" class="closebtn" data-dismiss="modal" aria-hidden="true">×</button>
84
                    <h3>Confirm deletion</h3>
85
                </div>
86
87
                <div class="modal-body">
88
                    <p>Are you sure you want to cancel this article request?</p>
89
90
                    <fieldset class="action">
91
                        [% SET ar_cancellation = AuthorisedValues.GetAuthValueDropbox('AR_CANCELLATION') %]
92
                        [% IF ar_cancellation %]
93
                            <label for="cancellation-reason" class="col-sm-4">Cancellation reason: </label>
94
                            <select class="cancellation-reason col-sm-8" name="cancellation-reason" id="modal-cancellation-reason">
95
                                <option value="" selected>Other reasons</option>
96
                                [% FOREACH reason IN ar_cancellation %]
97
                                    <option value="[% reason.authorised_value | html %]">[% reason.lib | html %]</option>
98
                                [% END %]
99
                            </select>
100
                        [% END %]
101
                    </fieldset>
102
                    <fieldset class="action">
103
                        <div class="hint col-sm-offset-4">Enter reason</div>
104
                        <input type="text" class="notes col-sm-offset-4 col-sm-8" name="notes" id="modal-notes"/>
105
                    </fieldset>
106
                </div>
107
108
                <div class="modal-footer">
109
                    <button id="cancelModalConfirmBtn" type="button" class="btn btn-danger" data-dismiss="modal">Confirm cancellation</button>
110
                    <a href="#" data-dismiss="modal">Cancel</a>
111
                </div>
112
            </div>
113
        </div>
114
    </div>
115
[% END %]
78
116
79
<body id="circ_article-requests" class="circ">
117
<body id="circ_article-requests" class="circ">
80
    [% INCLUDE 'header.inc' %]
118
    [% INCLUDE 'header.inc' %]
Lines 346-351 Link Here
346
            </div>
384
            </div>
347
        </div>
385
        </div>
348
    </div>
386
    </div>
387
    [% PROCESS cancel_modal %]
349
388
350
[% MACRO jsinclude BLOCK %]
389
[% MACRO jsinclude BLOCK %]
351
    [% INCLUDE 'datatables.inc' %]
390
    [% INCLUDE 'datatables.inc' %]
Lines 442-471 Link Here
442
            window.open(link, 'popup', 'width=600,height=400,resizable=1,toolbar=0,scrollbars=1,top');
481
            window.open(link, 'popup', 'width=600,height=400,resizable=1,toolbar=0,scrollbars=1,top');
443
        }
482
        }
444
483
484
        $('#modal-cancellation-reason').on('change', function(e) {
485
            let reason = $(this).val();
486
            $('#modal-notes').attr('disabled', !!reason);
487
        })
488
489
        // Confirm cancellation of article requests
490
        let cancel_id;
491
        let cancel_a;
492
        $("#cancelModalConfirmBtn").on("click",function(e) {
493
            let reason = $("#modal-cancellation-reason").val();
494
            let notes = $("#modal-notes").val();
495
            let query = '?'+(reason?'cancellation_reason='+reason:'notes='+notes)
496
497
            HandleMulti(function(id, a) {
498
                var table_row = a.closest('tr');
499
                table_row.find('.ar-process-request').remove();
500
                table_row.find('input[type="checkbox"]').prop('checked', false);
501
502
503
                a.closest('td').prepend('<img src="[% interface | html %]/[% theme | html %]/img/spinner-small.gif"/>').find('div.dropdown').hide();
504
505
                $.ajax({
506
                    type: "DELETE",
507
                    url: '/api/v1/article_requests/'+id+query,
508
                    success: function( data ) {
509
                        active_datatable.row( a.closest('tr') ).remove().draw();
510
                        UpdateTabCounts();
511
                        activateBatchActions( active_tab );
512
                    }
513
                });
514
            }, cancel_id, cancel_a)
515
        });
516
445
        function Cancel( id, a ) {
517
        function Cancel( id, a ) {
446
            // last_cancel_reason: undefined means 'prompt for new reason'
518
            cancel_id = id;
447
            // a null value indicates that prompt was cancelled
519
            cancel_a = a;
448
            if( last_cancel_reason === undefined ) last_cancel_reason = prompt(_("Please specify the reason for cancelling selected item(s):"));
449
            if ( last_cancel_reason === null ) {
450
                return;
451
            }
452
520
453
            a.closest('td').prepend('<img src="[% interface | html %]/[% theme | html %]/img/spinner-small.gif"/>').find('div.dropdown').hide();
521
            $('#cancelModal').modal();
454
            $.ajax({
455
                type: "POST",
456
                url: '/cgi-bin/koha/svc/article_request',
457
                data: {
458
                    action: 'cancel',
459
                    id: id,
460
                    notes: last_cancel_reason
461
                },
462
                success: function( data ) {
463
                    active_datatable.row( a.closest('tr') ).remove().draw();
464
                    UpdateTabCounts();
465
                    activateBatchActions( active_tab );
466
                },
467
                dataType: 'json'
468
            });
469
        }
522
        }
470
523
471
        function Process( id, a ) {
524
        function Process( id, a ) {
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/request-article.tt (-21 / +67 lines)
Lines 3-8 Link Here
3
[% USE KohaDates %]
3
[% USE KohaDates %]
4
[% USE Branches %]
4
[% USE Branches %]
5
[% USE ItemTypes %]
5
[% USE ItemTypes %]
6
[% USE AuthorisedValues %]
6
[% SET footerjs = 1 %]
7
[% SET footerjs = 1 %]
7
[% SET article_requests_view = 1 %]
8
[% SET article_requests_view = 1 %]
8
[% SET biblionumber = biblio.biblionumber %]
9
[% SET biblionumber = biblio.biblionumber %]
Lines 11-16 Link Here
11
[% INCLUDE 'doc-head-close.inc' %]
12
[% INCLUDE 'doc-head-close.inc' %]
12
</head>
13
</head>
13
14
15
[% BLOCK cancel_modal %]
16
    <div id="cancelModal" class="modal" tabindex="-1" role="dialog" aria-hidden="true">
17
        <div class="modal-dialog" role="document">
18
            <div class="modal-content">
19
                <div class="modal-header">
20
                    <button type="button" class="closebtn" data-dismiss="modal" aria-hidden="true">×</button>
21
                    <h3>Confirm deletion</h3>
22
                </div>
23
24
                <div class="modal-body">
25
                    <p>Are you sure you want to cancel this article request?</p>
26
27
                    <fieldset class="action">
28
                        [% SET ar_cancellation = AuthorisedValues.GetAuthValueDropbox('AR_CANCELLATION') %]
29
                        [% IF ar_cancellation %]
30
                            <label for="cancellation-reason" class="col-sm-4">Cancellation reason: </label>
31
                            <select class="cancellation-reason col-sm-8" name="cancellation-reason" id="modal-cancellation-reason">
32
                                <option value="" selected>Other reasons</option>
33
                                [% FOREACH reason IN ar_cancellation %]
34
                                    <option value="[% reason.authorised_value | html %]">[% reason.lib | html %]</option>
35
                                [% END %]
36
                            </select>
37
                        [% END %]
38
                    </fieldset>
39
                    <fieldset class="action">
40
                        <div class="hint col-sm-offset-4">Enter reason</div>
41
                        <input type="text" class="notes col-sm-offset-4 col-sm-8" name="notes" id="modal-notes"/>
42
                    </fieldset>
43
                </div>
44
45
                <div class="modal-footer">
46
                    <button id="cancelModalConfirmBtn" type="button" class="btn btn-danger" data-dismiss="modal">Confirm cancellation</button>
47
                    <a href="#" data-dismiss="modal">Cancel</a>
48
                </div>
49
            </div>
50
        </div>
51
    </div>
52
[% END %]
53
14
<body id="circ_request-article" class="circ">
54
<body id="circ_request-article" class="circ">
15
    [% INCLUDE 'header.inc' %]
55
    [% INCLUDE 'header.inc' %]
16
    [% INCLUDE 'circ-search.inc' %]
56
    [% INCLUDE 'circ-search.inc' %]
Lines 310-315 Link Here
310
                                [% END %]
350
                                [% END %]
311
                            </table>
351
                            </table>
312
                        </fieldset>
352
                        </fieldset>
353
                        [% PROCESS cancel_modal %]
313
                    [% END %]
354
                    [% END %]
314
355
315
            </main>
356
            </main>
Lines 394-422 Link Here
394
                }
435
                }
395
            });
436
            });
396
437
397
            $(".ar-cancel-request").on("click", function(){
438
            $('#modal-cancellation-reason').on('change', function(e) {
398
                var a = $(this);
439
                let reason = $(this).val();
399
                var notes = prompt(_("Reason for cancellation:"));
440
                $('#modal-notes').attr('disabled', !!reason);
441
            })
442
443
            let cancel_a;
444
            $("#cancelModalConfirmBtn").on("click",function(e) {
445
                var id = cancel_a.attr('id').split("cancel-")[1];
446
                $("#cancel-processing-" + id ).hide('slow');
447
                $("#cancel-processing-spinner-" + id ).show('slow');
448
449
                let reason = $("#modal-cancellation-reason").val();
450
                let notes = $("#modal-notes").val();
451
                let query = '?'+(reason?'cancellation_reason='+reason:'notes='+notes)
452
453
                $.ajax({
454
                    type: "DELETE",
455
                    url: '/api/v1/article_requests/'+id+query,
456
                    success: function( data ) {
457
                        cancel_a.parents('tr').hide('slow');
458
                    }
459
                });
460
            });
400
461
401
                if ( notes != null ) {
402
                    var id = this.id.split("cancel-")[1];
403
                    $("#cancel-processing-" + id ).hide('slow');
404
                    $("#cancel-processing-spinner-" + id ).show('slow');
405
462
406
                    $.ajax({
463
            $(".ar-cancel-request").on("click", function(){
407
                        type: "POST",
464
                cancel_a = $(this);
408
                        url: '/cgi-bin/koha/svc/article_request',
465
                $('#cancelModal').modal();
409
                        data: {
410
                            action: 'cancel',
411
                            id: id,
412
                            notes: notes
413
                        },
414
                        success: function( data ) {
415
                            a.parents('tr').hide('slow');
416
                        },
417
                        dataType: 'json'
418
                    });
419
                }
420
            });
466
            });
421
467
422
            // Initialize format(s)
468
            // Initialize format(s)
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt (-12 / +34 lines)
Lines 5-10 Link Here
5
[% USE Branches %]
5
[% USE Branches %]
6
[% USE ItemTypes %]
6
[% USE ItemTypes %]
7
[% USE Price %]
7
[% USE Price %]
8
[% USE AuthorisedValues %]
8
[% SET AdlibrisEnabled = Koha.Preference('AdlibrisCoversEnabled') %]
9
[% SET AdlibrisEnabled = Koha.Preference('AdlibrisCoversEnabled') %]
9
[% SET AdlibrisURL = Koha.Preference('AdlibrisCoversURL') %]
10
[% SET AdlibrisURL = Koha.Preference('AdlibrisCoversURL') %]
10
11
Lines 715-721 Link Here
715
                            <div id="opac-user-article-requests">
716
                            <div id="opac-user-article-requests">
716
                                [% IF logged_in_user.article_requests_current.count %]
717
                                [% IF logged_in_user.article_requests_current.count %]
717
                                    <table id="article-requests-table" class="table table-bordered table-striped">
718
                                    <table id="article-requests-table" class="table table-bordered table-striped">
718
                                        <caption>Article requests <span class="count">([% logged_in_user.article_requests_current.count | html %] total)</span></caption>
719
                                        <caption>Article requests <span class="count"></span></caption>
719
                                        <thead>
720
                                        <thead>
720
                                            <tr>
721
                                            <tr>
721
                                                <th class="anti-the article-request-record-title">Record title</th>
722
                                                <th class="anti-the article-request-record-title">Record title</th>
Lines 801-812 Link Here
801
                                                </td>
802
                                                </td>
802
803
803
                                                <td class="article-request-cancel">
804
                                                <td class="article-request-cancel">
804
                                                    <span class="tdlabel">Cancel:</span>
805
                                                    <button data-title="[% ar.biblio.title | html %] [% ar.item.enumchron | html %]" data-article-request_id="[% ar.id | html %]" class="btn btn-sm btn-danger btn-delete-article-request"><i class="fa fa-remove" aria-hidden="true"></i> Cancel</button>
805
                                                    <form action="/cgi-bin/koha/opac-article-request-cancel.pl" id="delete_article_request_[% ar.id | html %]">
806
                                                        <legend class="sr-only">Cancel article request</legend>
807
                                                        <input type="hidden" name="id" value="[% ar.id | html %]" />
808
                                                        <button data-title="[% ar.biblio.title | html %] [% ar.item.enumchron | html %]" data-article-request_id="[% ar.id | html %]" type="submit" class="btn btn-sm btn-danger btn-delete-article-request"><i class="fa fa-remove" aria-hidden="true"></i> Cancel</button>
809
                                                    </form>
810
                                                </td>
806
                                                </td>
811
                                            </tr>
807
                                            </tr>
812
                                        [% END %]
808
                                        [% END %]
Lines 843-848 Link Here
843
    [% INCLUDE 'calendar.inc' %]
839
    [% INCLUDE 'calendar.inc' %]
844
    [% INCLUDE 'datatables.inc' %]
840
    [% INCLUDE 'datatables.inc' %]
845
    <script>
841
    <script>
842
        var AR_CAPTION_COUNT = _("(%s total)");
843
844
846
        function tableInit( tableId ){
845
        function tableInit( tableId ){
847
            if( tableId == "checkoutst" ){
846
            if( tableId == "checkoutst" ){
848
                $(".dt-buttons").append("<button type=\"button\" class=\"dt-button buttons-ical\" id=\"buttons-ics\">iCal</button> ");
847
                $(".dt-buttons").append("<button type=\"button\" class=\"dt-button buttons-ical\" id=\"buttons-ics\">iCal</button> ");
Lines 852-857 Link Here
852
            }
851
            }
853
        }
852
        }
854
        $(document).ready(function(){
853
        $(document).ready(function(){
854
            $('#opac-user-article-requests caption .count').html(AR_CAPTION_COUNT.format('[% logged_in_user.article_requests_current.count | html %]'));
855
            $('#opac-user-views').tabs();
855
            $('#opac-user-views').tabs();
856
            $(".modal-nojs").addClass("modal").addClass("hide").removeClass("modal-nojs");
856
            $(".modal-nojs").addClass("modal").addClass("hide").removeClass("modal-nojs");
857
            $(".suspend-until").prop("readonly",1);
857
            $(".suspend-until").prop("readonly",1);
Lines 861-866 Link Here
861
                var hold_title = $(this).data("title");
861
                var hold_title = $(this).data("title");
862
                var reserve_id = $(this).data("reserve_id");
862
                var reserve_id = $(this).data("reserve_id");
863
                confirmModal( hold_title, _("Are you sure you want to cancel this hold?"), _("Yes, cancel hold"), _("No, do not cancel hold"), function( result ){
863
                confirmModal( hold_title, _("Are you sure you want to cancel this hold?"), _("Yes, cancel hold"), _("No, do not cancel hold"), function( result ){
864
                        $("#bootstrap-confirm-box-modal").remove()
864
                        if( result ){
865
                        if( result ){
865
                            $("#delete_hold_" + reserve_id ).submit();
866
                            $("#delete_hold_" + reserve_id ).submit();
866
                        }
867
                        }
Lines 872-883 Link Here
872
                e.preventDefault();
873
                e.preventDefault();
873
                var article_request = $(this).data("title");
874
                var article_request = $(this).data("title");
874
                var article_request_id = $(this).data("article-request_id");
875
                var article_request_id = $(this).data("article-request_id");
875
                confirmModal( article_request, _("Are you sure you want to cancel this article request?"), _("Yes, cancel article request"), _("No, do not cancel article request"), function( result ){
876
                (function(row){
877
                    var doCancel = function( result ){
878
                        $("#bootstrap-confirm-box-modal").remove();
876
                        if( result ){
879
                        if( result ){
877
                            $("#delete_article_request_" + article_request_id ).submit();
880
                            $.ajax({
881
                                type: "DELETE",
882
                                url: '/api/v1/public/patrons/'+borrowernumber+'/article_requests/'+article_request_id,
883
                                success: function( data ) {
884
                                    row.parents('tr').hide({
885
                                        duration: 'slow',
886
                                        complete: function() {
887
                                            var ar_tab = $('a[href="#opac-user-article-requests"');
888
                                            var ar_table = $('#article-requests-table');
889
                                            var ar_length = $('tbody tr:visible', ar_table).length;
890
                                            var ar_count = $('caption .count', ar_table);
891
892
                                            ar_tab.html(ar_tab.html().replace(/\(\d+\)/, '('+ar_length+')'));
893
                                            ar_count.html(AR_CAPTION_COUNT.format(ar_length));
894
                                        }
895
                                    });
896
                                }
897
                            });
878
                        }
898
                        }
879
                    }
899
                    };
880
                );
900
                    confirmModal( article_request, _("Are you sure you want to cancel this article request?"), _("Yes, cancel article request"), _("No, do not cancel article request"), doCancel);
901
                })($(this))
881
            });
902
            });
882
903
883
            $("#suspend_all_submit").on("click", function(e){
904
            $("#suspend_all_submit").on("click", function(e){
Lines 885-890 Link Here
885
                var title = _("Are you sure you want to suspend all holds?");
906
                var title = _("Are you sure you want to suspend all holds?");
886
                var body = _("All holds will be suspended.");
907
                var body = _("All holds will be suspended.");
887
                confirmModal( body, title, _("Yes, suspend all holds"), "", function( result ){
908
                confirmModal( body, title, _("Yes, suspend all holds"), "", function( result ){
909
                        $("#bootstrap-confirm-box-modal").remove()
888
                        if( result ){
910
                        if( result ){
889
                            $("#suspend_all_holds").submit();
911
                            $("#suspend_all_holds").submit();
890
                        }
912
                        }
Lines 897-902 Link Here
897
                var title = _("Are you sure you want to resume all suspended holds?");
919
                var title = _("Are you sure you want to resume all suspended holds?");
898
                var body = _("All holds will resume.");
920
                var body = _("All holds will resume.");
899
                confirmModal( body, title, _("Yes, resume all holds"), _("No, do not resume holds"), function( result ){
921
                confirmModal( body, title, _("Yes, resume all holds"), _("No, do not resume holds"), function( result ){
922
                        $("#bootstrap-confirm-box-modal").remove()
900
                        if( result ){
923
                        if( result ){
901
                            $("#resume_all_holds").submit();
924
                            $("#resume_all_holds").submit();
902
                        }
925
                        }
903
- 

Return to bug 27947