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

(-)a/koha-tmpl/opac-tmpl/bootstrap/en/includes/masthead.inc (+7 lines)
Lines 81-86 Link Here
81
                        </div> <!-- / .dropdown-menu -->
81
                        </div> <!-- / .dropdown-menu -->
82
                    </li> <!-- / .nav-item.dropdown -->
82
                    </li> <!-- / .nav-item.dropdown -->
83
                [% END # / IF virtualshelves %]
83
                [% END # / IF virtualshelves %]
84
                [% IF ( Koha.Preference( 'OpacTrustedCheckout' ) && loggedinusername ) %]
85
                    <li class="nav-item">
86
                        <a href="#" class="nav-link" title="Self check out" id="comenulink" role="button" data-toggle="modal" data-target="#checkoutModal">
87
                            <i id="checkout-icon" class="fa fa-barcode fa-icon-black" aria-hidden="true"></i> <span class="checkout-label">Self checkout</span>
88
                        </a>
89
                    </li>
90
                [% END %]
84
            </ul> <!-- / .navbar-nav -->
91
            </ul> <!-- / .navbar-nav -->
85
92
86
            [% IF Koha.Preference( 'opacuserlogin' ) == 1 ||  Koha.Preference( 'EnableOpacSearchHistory') || Koha.Preference( 'opaclanguagesdisplay' ) %]
93
            [% IF Koha.Preference( 'opacuserlogin' ) == 1 ||  Koha.Preference( 'EnableOpacSearchHistory') || Koha.Preference( 'opaclanguagesdisplay' ) %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/includes/modals/checkout.inc (+40 lines)
Line 0 Link Here
1
<!-- Checkout form hidden by default, used for modal window -->
2
<div class="modal" id="checkoutModal" tabindex="-1" role="dialog" aria-labelledby="checkoutModalLabel" aria-hidden="true">
3
    <div class="modal-dialog">
4
        <div class="modal-content">
5
            <div class="modal-header">
6
                <h3 class="modal-title" id="checkoutModalLabel">Self checkout</h3>
7
                <button type="button" class="closebtn" data-dismiss="modal" aria-label="Close">
8
                    <span aria-hidden="true">&times;</span>
9
                </button>
10
            </div>
11
            <div class="modal-body">
12
                <div id="checkoutResults"></div>
13
                <div id="availabilityResult"></div>
14
                <div class="form-group">
15
                    <label for="checkout_barcode">Enter item barcode: </label>
16
                    <input type="text" name="checkout_barcode" id="checkout_barcode" required="required"></input>
17
                </div>
18
                <table id="checkoutsTable" class="table table-bordered table-striped" style="width:100%;">
19
                    <thead>
20
                      <tr>
21
                          <th>Barcode</th>
22
                          <th>Due date</th>
23
                      </tr>
24
                    </thead>
25
                    <tbody>
26
                    </tbody>
27
                    <tfoot>
28
                      <tr>                    
29
                        <td colspan="2" class="text-right"><span id="checkoutsCount"></span> items checked out</td>
30
                      </tr>
31
                    </tfoot>
32
                </table>
33
            </div>
34
            <div class="modal-footer">
35
                <button type="submit" id="checkoutSubmit" class="btn btn-primary">Submit</button>
36
                <button type="button" id="checkoutClose" class="btn btn-default" data-dismiss="modal">Close</button>
37
            </div>
38
        </div>
39
    </div>
40
</div>
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-bottom.inc (+11 lines)
Lines 117-122 Link Here
117
    [% END # /IF OpacLangSelectorMode == 'both' || OpacLangSelectorMode == 'footer' %]
117
    [% END # /IF OpacLangSelectorMode == 'both' || OpacLangSelectorMode == 'footer' %]
118
[% END # / UNLESS is_popup %]
118
[% END # / UNLESS is_popup %]
119
119
120
[% IF ( Koha.Preference( 'OpacTrustedCheckout' ) && loggedinusername ) %]
121
    [% INCLUDE 'modals/checkout.inc' %]
122
[% END %]
123
120
<!-- JavaScript includes -->
124
<!-- JavaScript includes -->
121
[% Asset.js("lib/jquery/jquery-3.6.0.min.js") | $raw %]
125
[% Asset.js("lib/jquery/jquery-3.6.0.min.js") | $raw %]
122
[% Asset.js("lib/jquery/jquery-migrate-3.3.2.min.js") | $raw %]
126
[% Asset.js("lib/jquery/jquery-migrate-3.3.2.min.js") | $raw %]
Lines 124-129 Link Here
124
[% Asset.js("lib/bootstrap/js/bootstrap.bundle.min.js") | $raw %]
128
[% Asset.js("lib/bootstrap/js/bootstrap.bundle.min.js") | $raw %]
125
[% Asset.js("lib/fontfaceobserver.min.js") | $raw %]
129
[% Asset.js("lib/fontfaceobserver.min.js") | $raw %]
126
[% Asset.js("lib/enquire.min.js") | $raw %]
130
[% Asset.js("lib/enquire.min.js") | $raw %]
131
<script>
132
    let logged_in_user_id = "[% logged_in_user.borrowernumber | html %]";
133
</script>
127
[% Asset.js("js/global.js") | $raw %]
134
[% Asset.js("js/global.js") | $raw %]
128
[% IF ( OPACAmazonCoverImages || SyndeticsCoverImages ) %]
135
[% IF ( OPACAmazonCoverImages || SyndeticsCoverImages ) %]
129
    <script>
136
    <script>
Lines 288-293 $(document).ready(function() { Link Here
288
    }
295
    }
289
});
296
});
290
</script>
297
</script>
298
[% INCLUDE 'js-date-format.inc' %]
291
[% PROCESS jsinclude %]
299
[% PROCESS jsinclude %]
292
[% IF ( Koha.Preference('OPACUserJS') ) %]
300
[% IF ( Koha.Preference('OPACUserJS') ) %]
293
    <script>
301
    <script>
Lines 304-309 $(document).ready(function() { Link Here
304
        </script>
312
        </script>
305
    [% END %]
313
    [% END %]
306
[% END %]
314
[% END %]
315
[% IF ( Koha.Preference( 'OpacTrustedCheckout' ) && loggedinusername ) %]
316
    [% Asset.js("js/modals/checkout.js") | $raw %]
317
[% END %]
307
[% KohaPlugins.get_plugins_opac_js | $raw %]
318
[% KohaPlugins.get_plugins_opac_js | $raw %]
308
</body>
319
</body>
309
</html>
320
</html>
(-)a/koha-tmpl/opac-tmpl/bootstrap/js/modals/checkout.js (-1 / +233 lines)
Line 0 Link Here
0
- 
1
$(document).ready(function() {
2
3
    let checkouts_count = 0;
4
5
    function addResult(type, code, data) {
6
        let result = '';
7
        if (type == 'danger') {
8
            result += '<div class="alert alert-danger">';
9
        } else if (type == 'warning') {
10
            result += '<div class="alert alert-warning">';
11
        } else if (type == 'info') {
12
            result += '<div class="alert alert-info">';
13
        } else {
14
            result += '<div class="alert alert-success">';
15
        }
16
17
        if (code == 'NOT_FOUND') {
18
            result += _("Item '%s' not found").format(data);
19
        } else if (code == 'RENEW_ISSUE') {
20
            result += _("Item will be renewed").format(data);
21
        } else if (code == 'OTHER_CHARGES') {
22
            result += _("Your account currently has outstanding charges of '%s'").format(data);
23
        } else if (code == 'DEBT') {
24
            result += _("Your account is currently in debt by '%s'").format(data);
25
        } else if (code == 'ISSUED_TO_ANOTHER') {
26
            result += _("This item appears to be checked out to another patron, please return it to the desk");
27
        } else if (code == 'RESERVED' || code == 'RESERVED_WAITING') {
28
            result += _("This item appears to be reserved for another patron, please return it to the desk");
29
        } else if (code == 'TOO_MANY') {
30
            result += _("You have reached the maximum number of checkouts allowed on your account");
31
        } else if (code == 'AGE_RESTRICTION') {
32
            result += _("This item is age restricted");
33
        } else if (code == 'NO_MORE_RENEWALS') {
34
            result += _("Maximum renewals reached for this item");
35
        } else if (code == 'NOT_FOR_LOAN') {
36
            result += _("This item is not normally for loan, please select another or ask at the desk");
37
        } else if (code == 'WTHDRAWN') {
38
            result += _("This item is marked withdrawn, please select another or ask at the desk");
39
        } else if (code == 'EMPTY') {
40
            result += _("Please enter the barcode for the item you wish to checkout");
41
        } else {
42
            result += _("Message code '%s' with data '%s'").format(code, data);
43
        }
44
45
        result += '</div>';
46
        $('#availabilityResult').append(result);
47
    };
48
49
    // On modal show, clear any prior results and set focus
50
    $('#checkoutModal').on('shown.bs.modal', function(e) {
51
        $('#checkoutResults').replaceWith('<div id="checkoutResults"></div>');
52
        $('#availabilityResult').replaceWith('<div id="availabilityResult"></div>');
53
        $('#checkoutsTable').hide();
54
        $('#checkout_barcode').val("").focus();
55
    });
56
57
    // On modal submit
58
    $('#checkoutModal').on('click', '#checkoutSubmit', function(e) {
59
60
        // Get item from barcode
61
        let external_id = $('#checkout_barcode').val();
62
        if ( external_id === '' ) {
63
            addResult('warning', 'EMPTY');
64
            return;
65
        }
66
67
        let item_id;
68
        let items = $.ajax({
69
            url: '/api/v1/public/items?external_id=' + external_id,
70
            dataType: 'json',
71
            type: 'GET'
72
        });
73
74
        $('#availabilityResult').replaceWith('<div id="availabilityResult"></div>');
75
76
        // Get availability of the item
77
        let availability = items.then(
78
            function(data, textStatus, jqXHR) {
79
                if (data.length == 1) {
80
                    item_id = data[0].item_id;
81
                    return $.ajax({
82
                        url: '/api/v1/public/checkouts/availability?item_id=' + item_id + '&patron_id=' + logged_in_user_id,
83
                        type: 'GET',
84
                        contentType: "json"
85
                    });
86
                } else {
87
                    addResult('danger', 'NOT_FOUND', external_id);
88
                }
89
            },
90
            function(data, textStatus, jqXHR) {
91
                addResult('danger', 'NOT_FOUND', external_id);
92
                console.log('Items request failed with: ' + textStatus);
93
            }
94
        );
95
96
        let checkout = availability.then(
97
            function(data, textStatus, jqXHR) {
98
                let result;
99
                // blocked
100
                if (Object.keys(data.blockers).length >= 1) {
101
                    for (const key in data.blockers) {
102
                        if (data.blockers.hasOwnProperty(key)) {
103
                            addResult('danger', `${key}`, `${data.blockers[key]}`);
104
                            console.log(`${key}: ${data.blockers[key]}`);
105
                        }
106
                    }
107
                }
108
                // requires confirmation
109
                else if (Object.keys(data.confirms).length >= 1) {
110
                    for (const key in data.confirms) {
111
                        if (data.confirms.hasOwnProperty(key)) {
112
                            addResult('warning', `${key}`, `${data.confirms[key]}`);
113
                        }
114
                    }
115
                    $('#checkout_barcode').prop("readonly", true);
116
                    $('#checkoutSubmit').replaceWith('<input type="hidden" id="item_id" value="' + item_id + '"><input type="hidden" id="confirm_token" value="' + data.confirmation_token + '"><button type="submit" id="checkoutConfirm" class="btn btn-warning">Confirm</button>');
117
                }
118
                // straight checkout
119
                else {
120
                    let params = {
121
                        "checkout_id": undefined,
122
                        "patron_id": logged_in_user_id,
123
                        "item_id": item_id,
124
                        "due_date": undefined,
125
                        "library_id": undefined,
126
                        "issuer_id": undefined,
127
                        "checkin_date": undefined,
128
                        "last_renewed_date": undefined,
129
                        "renewals_count": undefined,
130
                        "unseen_renewals": undefined,
131
                        "auto_renew": undefined,
132
                        "auto_renew_error": undefined,
133
                        "timestamp": undefined,
134
                        "checkout_date": undefined,
135
                        "onsite_checkout": false,
136
                        "note": undefined,
137
                        "note_date": undefined,
138
                        "note_seen": undefined,
139
                    };
140
                    result = $.ajax({
141
                        url: '/api/v1/public/checkouts',
142
                        type: 'POST',
143
                        data: JSON.stringify(params),
144
                        contentType: "json"
145
                    });
146
                }
147
148
                // warnings to display
149
                if (Object.keys(data.warnings).length >= 1) {
150
                    for (const key in data.warnings) {
151
                        if (data.warnings.hasOwnProperty(key)) {
152
                            addResult('info', `${key}`, `${data.warnings[key]}`);
153
                        }
154
                    }
155
                }
156
157
                // return a rejected promise if we've reached here
158
                return result ? result : $.Deferred().reject('Checkout halted');
159
            },
160
            function(data, textStatus, jqXHR) {
161
                console.log('Items request failed with: ' + textStatus);
162
                console.log(data);
163
            }
164
        );
165
166
        checkout.then(
167
            function(data, textStatus, jqXHR) {
168
                $('#checkout_barcode').val("").prop("readonly", false).focus();
169
                $('#checkoutResults').replaceWith('<div id="checkoutResults" class="alert alert-success">' + _("Item '%s' was checked out").format(external_id) + '</div>');
170
                $('#checkoutsTable').show();
171
                $('#checkoutsTable > tbody').append('<tr><td>' + external_id +'</td><td>'+ $date(data.due_date) +'</td></tr>');
172
                checkouts_count++;
173
                $('#checkoutsCount').html(checkouts_count);
174
                // data retrieved from url2 as provided by the first request
175
            },
176
            function(data, textStatus, jqXHR) {
177
                console.log("checkout.then failed");
178
            }
179
        );
180
181
    });
182
183
    $('#checkoutModal').on('click', '#checkoutConfirm', function(e) {
184
        let external_id = $('#checkout_barcode').val();
185
        let item_id = $('#item_id').val();
186
        let token = $('#confirm_token').val();
187
        let params = {
188
            "checkout_id": undefined,
189
            "patron_id": logged_in_user_id,
190
            "item_id": item_id,
191
            "due_date": undefined,
192
            "library_id": undefined,
193
            "issuer_id": undefined,
194
            "checkin_date": undefined,
195
            "last_renewed_date": undefined,
196
            "renewals_count": undefined,
197
            "unseen_renewals": undefined,
198
            "auto_renew": undefined,
199
            "auto_renew_error": undefined,
200
            "timestamp": undefined,
201
            "checkout_date": undefined,
202
            "onsite_checkout": false,
203
            "note": undefined,
204
            "note_date": undefined,
205
            "note_seen": undefined,
206
        };
207
        let checkout = $.ajax({
208
            url: '/api/v1/public/checkouts?confirmation=' + token,
209
            type: 'POST',
210
            data: JSON.stringify(params),
211
            contentType: "json"
212
        });
213
214
        checkout.then(
215
            function(data, textStatus, jqXHR) {
216
                $('#item_id').remove;
217
                $('#confirm_token').remove;
218
                $('#availabilityResult').replaceWith('<div id="availabilityResult"></div>');
219
                $('#checkoutResults').replaceWith('<div id="checkoutResults" class="alert alert-success">' + _("Item '%s' was checked out").format(external_id) + '</div>');
220
                $('#checkout_barcode').val("").prop("readonly", false).focus();
221
                $('#checkoutsTable').show();
222
                $('#checkoutsTable > tbody').append('<tr><td>' + external_id +'</td><td>'+ $date(data.due_date) +'</td></tr>');
223
                $('#checkoutConfirm').replaceWith('<button type="submit" id="checkoutSubmit" class="btn btn-primary">Submit</button>');
224
                checkouts_count++;
225
                $('#checkoutsCount').html(checkouts_count);
226
                // data retrieved from url2 as provided by the first request
227
            },
228
            function(data, textStatus, jqXHR) {
229
                console.log("checkout.then failed");
230
            }
231
        );
232
    });
233
});

Return to bug 30979