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

(-)a/C4/Circulation.pm (+6 lines)
Lines 2311-2316 sub AddReturn { Link Here
2311
        }
2311
        }
2312
    }
2312
    }
2313
2313
2314
    # Check for bundle status
2315
    if ( $item->in_bundle ) {
2316
        my $host = $item->bundle_host;
2317
        $messages->{InBundle} = $host;
2318
    }
2319
2314
    my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
2320
    my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
2315
    $indexer->index_records( $item->biblionumber, "specialUpdate", "biblioserver" );
2321
    $indexer->index_records( $item->biblionumber, "specialUpdate", "biblioserver" );
2316
2322
(-)a/Koha/Item.pm (+16 lines)
Lines 397-402 sub checkout { Link Here
397
    return Koha::Checkout->_new_from_dbic( $checkout_rs );
397
    return Koha::Checkout->_new_from_dbic( $checkout_rs );
398
}
398
}
399
399
400
=head3 last_checkout
401
402
  my $old_checkout = $item->last_checkout;
403
404
Return the last_checkout for this item
405
406
=cut
407
408
sub last_checkout {
409
    my ( $self ) = @_;
410
    my $checkout_rs = $self->_result->old_issues->search( {},
411
        { order_by => { '-desc' => 'returndate' }, rows => 1 } )->single;
412
    return unless $checkout_rs;
413
    return Koha::Old::Checkout->_new_from_dbic( $checkout_rs );
414
}
415
400
=head3 holds
416
=head3 holds
401
417
402
my $holds = $item->holds();
418
my $holds = $item->holds();
(-)a/circ/returns.pl (-4 / +60 lines)
Lines 38-44 use C4::Auth qw( get_template_and_user get_session haspermission ); Link Here
38
use C4::Output qw( output_html_with_http_headers );
38
use C4::Output qw( output_html_with_http_headers );
39
use C4::Circulation qw( barcodedecode GetBranchItemRule AddReturn updateWrongTransfer LostItem );
39
use C4::Circulation qw( barcodedecode GetBranchItemRule AddReturn updateWrongTransfer LostItem );
40
use C4::Reserves qw( ModReserve ModReserveAffect GetOtherReserves );
40
use C4::Reserves qw( ModReserve ModReserveAffect GetOtherReserves );
41
use C4::Circulation qw( barcodedecode GetBranchItemRule AddReturn updateWrongTransfer LostItem );
42
use C4::Context;
41
use C4::Context;
43
use C4::Items qw( ModItemTransfer );
42
use C4::Items qw( ModItemTransfer );
44
use C4::Members::Messaging;
43
use C4::Members::Messaging;
Lines 301-310 if ($barcode) { Link Here
301
    $template->param( 'multiple_confirmed' => 1 )
300
    $template->param( 'multiple_confirmed' => 1 )
302
      if $query->param('multiple_confirm');
301
      if $query->param('multiple_confirm');
303
302
303
    # Block return if bundle and confirm has not been received
304
    my $bundle_confirm =
305
         $item
306
      && $item->is_bundle
307
      && !$query->param('confirm_items_bundle_return');
308
    $template->param( 'confirm_items_bundle_returned' => 1 )
309
      if $query->param('confirm_items_bundle_return');
310
304
    # do the return
311
    # do the return
305
    ( $returned, $messages, $issue, $borrower ) =
312
    ( $returned, $messages, $issue, $borrower ) =
306
      AddReturn( $barcode, $userenv_branch, $exemptfine, $return_date )
313
      AddReturn( $barcode, $userenv_branch, $exemptfine, $return_date )
307
          unless $needs_confirm;
314
          unless ( $needs_confirm || $bundle_confirm );
308
315
309
    if ($returned) {
316
    if ($returned) {
310
        my $time_now = dt_from_string()->truncate( to => 'minute');
317
        my $time_now = dt_from_string()->truncate( to => 'minute');
Lines 345-351 if ($barcode) { Link Here
345
                );
352
                );
346
            }
353
            }
347
        }
354
        }
348
    } elsif ( C4::Context->preference('ShowAllCheckins') and !$messages->{'BadBarcode'} and !$needs_confirm ) {
355
356
        # Mark missing bundle items as lost and report unexpected items
357
        if ( $item->is_bundle ) {
358
            my $BundleLostValue = C4::Context->preference('BundleLostValue');
359
            my $checkout = $item->last_checkout;
360
            my $barcodes = $query->param('verify-items-bundle-contents-barcodes');
361
            my @barcodes = map { s/^\s+|\s+$//gr } ( split /\n/, $barcodes );
362
            my $expected_items = { map { $_->barcode => $_ } $item->bundle_items->as_list };
363
            my $verify_items = Koha::Items->search( { barcode => { 'in' => \@barcodes } } );
364
            my @unexpected_items;
365
            my @missing_items;
366
            my @bundle_items;
367
            while ( my $verify_item = $verify_items->next ) {
368
                # Fix and lost statuses
369
                $verify_item->itemlost(0);
370
371
                # Expected item, remove from lookup table
372
                if ( delete $expected_items->{$verify_item->barcode} ) {
373
                    push @bundle_items, $verify_item;
374
                }
375
                # Unexpected item, warn and remove from bundle
376
                else {
377
                    $verify_item->remove_from_bundle;
378
                    push @unexpected_items, $verify_item;
379
                }
380
                # Store results
381
                $verify_item->store();
382
            }
383
            for my $missing_item ( keys %{$expected_items} ) {
384
                my $bundle_item = $expected_items->{$missing_item};
385
                $bundle_item->itemlost($BundleLostValue)->store();
386
                push @missing_items, $bundle_item;
387
            }
388
            $template->param(
389
                unexpected_items => \@unexpected_items,
390
                missing_items    => \@missing_items,
391
                bundle_items     => \@bundle_items
392
            );
393
        }
394
    } elsif ( C4::Context->preference('ShowAllCheckins') and !$messages->{'BadBarcode'} and !$needs_confirm and !$bundle_confirm ) {
349
        $input{duedate}   = 0;
395
        $input{duedate}   = 0;
350
        $returneditems{0} = $barcode;
396
        $returneditems{0} = $barcode;
351
        $riduedate{0}     = 0;
397
        $riduedate{0}     = 0;
Lines 356-361 if ($barcode) { Link Here
356
    if ( $needs_confirm ) {
402
    if ( $needs_confirm ) {
357
        $template->param( needs_confirm => $needs_confirm );
403
        $template->param( needs_confirm => $needs_confirm );
358
    }
404
    }
405
406
    if ( $bundle_confirm ) {
407
        $template->param(
408
            items_bundle_return_confirmation => 1,
409
        );
410
    }
359
}
411
}
360
$template->param( inputloop => \@inputloop );
412
$template->param( inputloop => \@inputloop );
361
413
Lines 553-559 foreach my $code ( keys %$messages ) { Link Here
553
    }
605
    }
554
    elsif ( $code eq 'ReturnClaims' ) {
606
    elsif ( $code eq 'ReturnClaims' ) {
555
        $template->param( ReturnClaims => $messages->{ReturnClaims} );
607
        $template->param( ReturnClaims => $messages->{ReturnClaims} );
556
    } else {
608
    }
609
    elsif ( $code eq 'InBundle' ) {
610
        $template->param( InBundle => $messages->{InBundle} );
611
    }
612
    else {
557
        die "Unknown error code $code";    # note we need all the (empty) elsif's above, or we die.
613
        die "Unknown error code $code";    # note we need all the (empty) elsif's above, or we die.
558
        # This forces the issue of staying in sync w/ Circulation.pm
614
        # This forces the issue of staying in sync w/ Circulation.pm
559
    }
615
    }
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/item-status.inc (+84 lines)
Line 0 Link Here
1
[% USE AuthorisedValues %]
2
[% USE Branches %]
3
[% USE Koha %]
4
[% USE KohaDates %]
5
[% PROCESS 'i18n.inc' %]
6
[% transfer = item.get_transfer() %]
7
[% IF item.checkout %]
8
    <span class="datedue">
9
        [% IF item.checkout.onsite_checkout %]
10
            [% t('Currently in local use by') | html %]
11
        [% ELSE %]
12
            [% t('Checked out to') | html %]
13
        [% END %]
14
        [% INCLUDE 'patron-title.inc' patron=item.checkout.patron hide_patron_infos_if_needed=1 %]
15
        : [% tx('due {date_due}', { date_due = item.checkout.date_due }) | html %]
16
    </span>
17
[% ELSIF transfer %]
18
    [% datesent = BLOCK %][% transfer.datesent | $KohaDates %][% END %]
19
    <span class="intransit">[% tx('In transit from {frombranch} to {tobranch} since {datesent}', { frombranch = Branches.GetName(transfer.frombranch), tobranch = Branches.GetName(transfer.tobranch), datesent = datesent }) %]</span>
20
[% END %]
21
22
[% IF item.itemlost %]
23
    [% itemlost_description = AuthorisedValues.GetDescriptionByKohaField({ kohafield = 'items.itemlost', authorised_value = item.itemlost }) %]
24
    [% IF itemlost_description %]
25
        <span class="lost">[% itemlost_description | html %]</span>
26
    [% ELSE %]
27
        <span class="lost">[% t('Unavailable (lost or missing)') | html %]</span>
28
    [% END %]
29
[% END %]
30
31
[% IF item.withdrawn %]
32
    [% withdrawn_description = AuthorisedValues.GetDescriptionByKohaField({ kohafield = 'items.withdrawn', authorised_value = item.withdrawn }) %]
33
    [% IF withdrawn_description %]
34
        <span class="wdn">[% withdrawn_description | html %]</span>
35
    [% ELSE %]
36
        <span class="wdn">[% t('Withdrawn') | html %]</span>
37
    [% END %]
38
[% END %]
39
40
[% IF item.damaged %]
41
    [% damaged_description = AuthorisedValues.GetDescriptionByKohaField({ kohafield = 'items.damaged', authorised_value = item.damaged }) %]
42
    [% IF damaged_description %]
43
        <span class="dmg">[% damaged_description | html %]</span>
44
    [% ELSE %]
45
        <span class="dmg">[% t('Damaged') | html %]</span>
46
    [% END %]
47
[% END %]
48
49
[% IF item.notforloan || item.effective_itemtype.notforloan %]
50
    <span>
51
        [% t('Not for loan') | html %]
52
        [% notforloan_description = AuthorisedValues.GetDescriptionByKohaField({ kohafield = 'items.notforloan', authorised_value = item.notforloan }) %]
53
        [% IF notforloan_description %]
54
            ([% notforloan_description | html %])
55
        [% END %]
56
    </span>
57
[% END %]
58
59
[% hold = item.holds.next %]
60
[% IF hold %]
61
    <span>
62
        [% IF hold.waitingdate %]
63
            Waiting at [% Branches.GetName(hold.get_column('branchcode')) | html %] since [% hold.waitingdate | $KohaDates %].
64
        [% ELSE %]
65
            Item-level hold (placed [% hold.reservedate | $KohaDates %]) for delivery at [% Branches.GetName(hold.get_column('branchcode')) | html %].
66
        [% END %]
67
        [% IF Koha.Preference('canreservefromotherbranches') %]
68
            [% t('Hold for:') | html %]
69
            [% INCLUDE 'patron-title.inc' patron=hold.borrower hide_patron_infos_if_needed=1 %]
70
        [% END %]
71
    </span>
72
[% END %]
73
[% UNLESS item.notforloan || item.effective_itemtype.notforloan || item.onloan || item.itemlost || item.withdrawn || item.damaged || transfer || hold %]
74
    <span>[% t('Available') | html %]</span>
75
[% END %]
76
77
[% IF ( item.restricted ) %]
78
    [% restricted_description = AuthorisedValues.GetDescriptionByKohaField({ kohafield = 'items.restricted', authorised_value = item.restricted }) %]
79
    [% IF restricted_description %]
80
        <span class="restricted">([% restricted_description | html %])</span>
81
    [% ELSE %]
82
        <span class="restricted">[% t('Restricted') | html %]</span>
83
    [% END %]
84
[% END %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/modals/bundle_contents.inc (+35 lines)
Line 0 Link Here
1
<!-- Bundle contents modal -->
2
<div class="modal printable" id="bundleContentsModal" tabindex="-1" role="dialog" aria-labelledby="bundleContentsLabel">
3
    <div class="modal-dialog" role="document">
4
        <div class="modal-content">
5
            <div class="modal-header">
6
                <button type="button" class="closebtn" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
7
                <h4 class="modal-title" id="bundleContentsLabel">Bundle contents</h4>
8
            </div>
9
            <div class="modal-body">
10
                <table style="width:100%">
11
                    <thead>
12
                        <tr>
13
                            <th>Barcode</th>
14
                            <th>Title</th>
15
                        </tr>
16
                    </thead>
17
                    <tbody>
18
                    [% FOREACH bundle_item IN bundle_items %]
19
                        <tr>
20
                            <td>[% bundle_item.barcode | html %]</td>
21
                            <td>[% INCLUDE 'biblio-title.inc' biblio=bundle_item.biblio %]</td>
22
                        </tr>
23
                    [% END %]
24
                    </tbody>
25
                    <tfoot>
26
                    </tfoot>
27
                </table>
28
            </div> <!-- /.modal-body -->
29
            <div class="modal-footer">
30
                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
31
                <button type="button" class="printModal btn btn-primary"><i class="fa fa-print"></i> Print</button>
32
            </div> <!-- /.modal-footer -->
33
        </div> <!-- /.modal-content -->
34
    </div> <!-- /.modal-dialog -->
35
</div> <!-- /#bundleContentsModal -->
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref (+4 lines)
Lines 12-17 Circulation: Link Here
12
                  1: "Require"
12
                  1: "Require"
13
                  0: "Don't require"
13
                  0: "Don't require"
14
            - staff to confirm that all parts of an item are present at checkin/checkout.
14
            - staff to confirm that all parts of an item are present at checkin/checkout.
15
        -
16
            - Use the LOST authorised value
17
            - pref: BundleLostValue
18
            - to represent 'missing from bundle' at return.
15
        -
19
        -
16
            - pref: AutoSwitchPatron
20
            - pref: AutoSwitchPatron
17
              choices:
21
              choices:
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt (-1 / +138 lines)
Lines 6-11 Link Here
6
[% USE ItemTypes %]
6
[% USE ItemTypes %]
7
[% USE AuthorisedValues %]
7
[% USE AuthorisedValues %]
8
[% USE TablesSettings %]
8
[% USE TablesSettings %]
9
[% PROCESS 'i18n.inc' %]
9
[% PROCESS 'member-display-address-style.inc' %]
10
[% PROCESS 'member-display-address-style.inc' %]
10
[% SET footerjs = 1 %]
11
[% SET footerjs = 1 %]
11
[% BLOCK display_bormessagepref %]
12
[% BLOCK display_bormessagepref %]
Lines 217-222 Link Here
217
                                </div>
218
                                </div>
218
                            [% END %]
219
                            [% END %]
219
220
221
                            <!-- Bundle has items missing -->
222
                            [% IF missing_items %]
223
                                <div id="bundle_missing_items" class="dialog alert">
224
                                    <h3>Bundle had missing items</h3>
225
                                    <p>Bundle contents list updated</p>
226
                                    <p><a class="btn btn-default btn-xs" role="button" data-toggle="modal" href="#bundleContentsModal"><i class="fa fa-eye" aria-hidden="true"></i> View updated contents list</a></p>
227
                                </div>
228
                            [% END %]
229
230
                            <!-- Bundle contained unexpected items -->
231
                            [% IF unexpected_items %]
232
                                <div id="bundle_unexpected_items" class="dialog alert">
233
                                    <h3>Bundle had unexpected items</h3>
234
                                    <p>Please place the following items to one side</p>
235
                                    <ul>
236
                                    [% FOREACH unexpected_item IN unexpected_items %]
237
                                        <li>[% INCLUDE 'biblio-title.inc' biblio=unexpected_item.biblio %] - [% unexpected_item.barcode | html %]</li>
238
                                    [% END %]
239
                                    </ul>
240
                                </div>
241
                            [% END %]
242
243
                            <!-- Item checked in outside of bundle -->
244
                            [% IF InBundle %]
245
                                <div id="" class="dialog alert audio-alert-warning">
246
                                    <h3>Item belongs in bundle</h3>
247
                                    <p>This item belongs to a bundle: [% INCLUDE 'biblio-title.inc' biblio=InBundle.biblio %] - [% InBundle.barcode | html %]</p>
248
                                    <p><button class="btn btn-default btn-xs bundle_remove" role="button" data-itemnumber="[% itemnumber | uri %]" data-hostnumber="[% InBundle.itemnumber | uri %]"><i class="fa fa-minus"></i> Remove from bundle</button></p>
249
                                </div>
250
                            [% END %]
220
251
221
                            [% IF ( errmsgloop ) %]
252
                            [% IF ( errmsgloop ) %]
222
                                <div class="dialog alert audio-alert-warning">
253
                                <div class="dialog alert audio-alert-warning">
Lines 381-386 Link Here
381
                            </div>
412
                            </div>
382
                        [% END %]
413
                        [% END %]
383
414
415
                        [% IF items_bundle_return_confirmation %]
416
                        <div id="bundle-needsconfirmation-modal" class="modal fade audio-alert-action block">
417
                            <div class="modal-dialog modal-wide">
418
                                <div class="modal-content">
419
                                    <form method="post">
420
                                        <div class="modal-header">
421
                                            <h3>Please confirm bundle contents</h3>
422
                                        </div>
423
                                        <div class="modal-body">
424
425
                                            <table class="table table-condensed table-bordered" id="items-bundle-contents-table">
426
                                                <thead>
427
                                                    <tr>
428
                                                        <th>[% t('Title') | html %]</th>
429
                                                        <th>[% t('Author') | html %]</th>
430
                                                        <th>[% t('Collection code') | html %]</th>
431
                                                        <th>[% t('Item type') | html %]</th>
432
                                                        <th>[% t('Callnumber') | html %]</th>
433
                                                        <th>[% t('Barcode') | html %]</th>
434
                                                        <th>[% t('Status') | html %]</th>
435
                                                    </tr>
436
                                                </thead>
437
                                                <tbody>
438
                                                    [% FOREACH bundle_item IN item.bundle_items %]
439
                                                    <tr data-barcode="[% bundle_item.barcode | html %]">
440
                                                        <td>[% INCLUDE 'biblio-title.inc' biblio=bundle_item.biblio link = 1 %]</td>
441
                                                        <td>[% bundle_item.biblio.author | html %]</td>
442
                                                        <td>[% AuthorisedValues.GetByCode('CCODE', bundle_item.ccode) | html %]</td>
443
                                                        <td>[% ItemTypes.GetDescription(bundle_item.itype) | html %]</td>
444
                                                        <td>[% bundle_item.itemcallnumber | html %]</td>
445
                                                        <td>[% bundle_item.barcode | html %]</td>
446
                                                        <td>[% INCLUDE 'item-status.inc' item=bundle_item %]</td>
447
                                                    </tr>
448
                                                    [% END %]
449
                                                </tbody>
450
                                            </table>
451
452
                                            <div class="form-group">
453
                                                <label for="verify-items-bundle-contents-barcodes">Barcodes</label>
454
                                                <textarea autocomplete="off" id="verify-items-bundle-contents-barcodes" name="verify-items-bundle-contents-barcodes" class="form-control"></textarea>
455
                                                <div class="help-block">[% t('Scan all barcodes of items found in the items bundle. If any items are missing, they will be marked as lost') | html %]</div>
456
                                            </div>
457
458
                                        </div>
459
                                        <div class="modal-footer">
460
                                            <input type="hidden" name="barcode" value="[% item.barcode | html %]">
461
                                            <input type="hidden" name="confirm_items_bundle_return" value="1">
462
                                            [% FOREACH inputloo IN inputloop %]
463
                                            <input type="hidden" name="ri-[% inputloo.counter | html %]" value="[% inputloo.barcode | html %]" />
464
                                            <input type="hidden" name="dd-[% inputloo.counter | html %]" value="[% inputloo.duedate | html %]" />
465
                                            <input type="hidden" name="bn-[% inputloo.counter | html %]" value="[% inputloo.borrowernumber | html %]" />
466
                                            [% END %]
467
                                            <button type="submit" class="btn btn-default"><i class="fa fa-check"></i> [% t('Confirm checkin and mark missing items as lost') | html %]</button>
468
                                            <button type="button" data-dismiss="modal" class="btn btn-default"><i class="fa fa-close"></i> [% t('Cancel') | html %]</button>
469
                                        </div>
470
                                    </form>
471
                                </div>
472
                            </div>
473
                        </div>
474
                        [% END %]
475
384
                        [% IF wrongbranch %]
476
                        [% IF wrongbranch %]
385
                            <div id="wrong-branch-modal" class="modal fade audio-alert-action block">
477
                            <div id="wrong-branch-modal" class="modal fade audio-alert-action block">
386
                                <div class="modal-dialog">
478
                                <div class="modal-dialog">
Lines 986-991 Link Here
986
            </div> <!-- /.col-sm-12 -->
1078
            </div> <!-- /.col-sm-12 -->
987
        </div> <!-- /.row -->
1079
        </div> <!-- /.row -->
988
1080
1081
    [% INCLUDE 'modals/bundle_contents.inc' %]
1082
989
[% MACRO jsinclude BLOCK %]
1083
[% MACRO jsinclude BLOCK %]
990
    [% INCLUDE 'datatables.inc' %]
1084
    [% INCLUDE 'datatables.inc' %]
991
    [% INCLUDE 'columns_settings.inc' %]
1085
    [% INCLUDE 'columns_settings.inc' %]
Lines 1188-1195 Link Here
1188
                window.open("/cgi-bin/koha/members/printslip.pl?borrowernumber=" + borrowernumber + "&amp;print=checkinslip", "printwindow");
1282
                window.open("/cgi-bin/koha/members/printslip.pl?borrowernumber=" + borrowernumber + "&amp;print=checkinslip", "printwindow");
1189
            });
1283
            });
1190
1284
1285
            // item bundles
1286
            $('#verify-items-bundle-contents-barcodes').on('input', function (ev) {
1287
                const barcodes = ev.target.value.split('\n').map(function(s) { return s.trim() });
1288
                $('#items-bundle-contents-table tr').each(function () {
1289
                    const barcode = this.getAttribute('data-barcode');
1290
                    if (barcodes.includes(barcode)) {
1291
                        this.classList.add('ok');
1292
                    } else {
1293
                        this.classList.remove('ok');
1294
                    }
1295
                })
1296
            });
1297
1298
            $('.bundle_remove').on('click', function() {
1299
                var component_itemnumber = $(this).data('itemnumber');
1300
                var host_itemnumber = $(this).data('hostnumber');
1301
                var alert = $(this).closest('div');
1302
                var unlink_item_url = "/api/v1/items/" + host_itemnumber + "/bundled_items/item/" + component_itemnumber;
1303
                $.ajax({
1304
                    type: "DELETE",
1305
                    url: unlink_item_url,
1306
                    success: function(){
1307
                        alert.remove();
1308
                    }
1309
                });
1310
            });
1311
1312
            // print modals
1313
            $('.modal.printable').on('shown.bs.modal', function() {
1314
                $('.modal-dialog', this).addClass('focused');
1315
                $('body').addClass('modalprinter');
1316
1317
                if ($(this).hasClass('autoprint')) {
1318
                    window.print();
1319
                }
1320
            }).on('hidden.bs.modal', function() {
1321
                $('.modal-dialog', this).removeClass('focused');
1322
                $('body').removeClass('modalprinter');
1323
            });
1324
1325
            $('.printModal').click(function() {
1326
                window.print();
1327
            });
1191
        });
1328
        });
1192
    </script>
1329
    </script>
1330
1193
[% END %]
1331
[% END %]
1194
1332
1195
[% INCLUDE 'intranet-bottom.inc' %]
1333
[% INCLUDE 'intranet-bottom.inc' %]
1196
- 

Return to bug 28854