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

(-)a/C4/Circulation.pm (+6 lines)
Lines 2433-2438 sub AddReturn { Link Here
2433
        }
2433
        }
2434
    }
2434
    }
2435
2435
2436
    # Check for bundle status
2437
    if ( $item->in_bundle ) {
2438
        my $host = $item->bundle_host;
2439
        $messages->{InBundle} = $host;
2440
    }
2441
2436
    my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
2442
    my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
2437
    $indexer->index_records( $item->biblionumber, "specialUpdate", "biblioserver" );
2443
    $indexer->index_records( $item->biblionumber, "specialUpdate", "biblioserver" );
2438
2444
(-)a/circ/returns.pl (-2 / +56 lines)
Lines 338-347 if ($barcode) { Link Here
338
    $template->param( 'multiple_confirmed' => 1 )
338
    $template->param( 'multiple_confirmed' => 1 )
339
      if $query->param('multiple_confirm');
339
      if $query->param('multiple_confirm');
340
340
341
    # Block return if bundle and confirm has not been received
342
    my $bundle_confirm =
343
         $item
344
      && $item->is_bundle
345
      && !$query->param('confirm_items_bundle_return');
346
    $template->param( 'confirm_items_bundle_returned' => 1 )
347
      if $query->param('confirm_items_bundle_return');
348
341
    # do the return
349
    # do the return
342
    ( $returned, $messages, $issue, $borrower ) =
350
    ( $returned, $messages, $issue, $borrower ) =
343
      AddReturn( $barcode, $userenv_branch, $exemptfine, $return_date )
351
      AddReturn( $barcode, $userenv_branch, $exemptfine, $return_date )
344
          unless $needs_confirm;
352
          unless ( $needs_confirm || $bundle_confirm );
345
353
346
    if ($returned) {
354
    if ($returned) {
347
        my $time_now = dt_from_string()->truncate( to => 'minute');
355
        my $time_now = dt_from_string()->truncate( to => 'minute');
Lines 382-388 if ($barcode) { Link Here
382
                );
390
                );
383
            }
391
            }
384
        }
392
        }
385
    } elsif ( C4::Context->preference('ShowAllCheckins') and !$messages->{'BadBarcode'} and !$needs_confirm ) {
393
394
        # Mark missing bundle items as lost and report unexpected items
395
        if ( $item->is_bundle ) {
396
            my $BundleLostValue = C4::Context->preference('BundleLostValue');
397
            my $barcodes = $query->param('verify-items-bundle-contents-barcodes');
398
            my @barcodes = map { s/^\s+|\s+$//gr } ( split /\n/, $barcodes );
399
            my $expected_items = { map { $_->barcode => $_ } $item->bundle_items->as_list };
400
            my $verify_items = Koha::Items->search( { barcode => { 'in' => \@barcodes } } );
401
            my @unexpected_items;
402
            my @missing_items;
403
            my @bundle_items;
404
            while ( my $verify_item = $verify_items->next ) {
405
                # Fix and lost statuses
406
                $verify_item->itemlost(0);
407
408
                # Expected item, remove from lookup table
409
                if ( delete $expected_items->{$verify_item->barcode} ) {
410
                    push @bundle_items, $verify_item;
411
                }
412
                # Unexpected item, warn and remove from bundle
413
                else {
414
                    $verify_item->remove_from_bundle;
415
                    push @unexpected_items, $verify_item;
416
                }
417
                # Store results
418
                $verify_item->store();
419
            }
420
            for my $missing_item ( keys %{$expected_items} ) {
421
                my $bundle_item = $expected_items->{$missing_item};
422
                $bundle_item->itemlost($BundleLostValue)->store();
423
                push @missing_items, $bundle_item;
424
            }
425
            $template->param(
426
                unexpected_items => \@unexpected_items,
427
                missing_items    => \@missing_items,
428
                bundle_items     => \@bundle_items
429
            );
430
        }
431
    } elsif ( C4::Context->preference('ShowAllCheckins') and !$messages->{'BadBarcode'} and !$needs_confirm and !$bundle_confirm ) {
386
        $input{duedate}   = 0;
432
        $input{duedate}   = 0;
387
        $returneditems{0} = $barcode;
433
        $returneditems{0} = $barcode;
388
        $riduedate{0}     = 0;
434
        $riduedate{0}     = 0;
Lines 393-398 if ($barcode) { Link Here
393
    if ( $needs_confirm ) {
439
    if ( $needs_confirm ) {
394
        $template->param( needs_confirm => $needs_confirm );
440
        $template->param( needs_confirm => $needs_confirm );
395
    }
441
    }
442
443
    if ( $bundle_confirm ) {
444
        $template->param(
445
            items_bundle_return_confirmation => 1,
446
        );
447
    }
396
}
448
}
397
$template->param( inputloop => \@inputloop );
449
$template->param( inputloop => \@inputloop );
398
450
Lines 631-636 foreach my $code ( keys %$messages ) { Link Here
631
        ;
683
        ;
632
    } elsif ( $code eq 'TransferredRecall' ) {
684
    } elsif ( $code eq 'TransferredRecall' ) {
633
        ;
685
        ;
686
    } elsif ( $code eq 'InBundle' ) {
687
        $template->param( InBundle => $messages->{InBundle} );
634
    } else {
688
    } else {
635
        die "Unknown error code $code";    # note we need all the (empty) elsif's above, or we die.
689
        die "Unknown error code $code";    # note we need all the (empty) elsif's above, or we die.
636
        # This forces the issue of staying in sync w/ Circulation.pm
690
        # This forces the issue of staying in sync w/ Circulation.pm
(-)a/koha-tmpl/intranet-tmpl/prog/css/src/_tables.scss (-1 / +1 lines)
Lines 359-365 tbody { Link Here
359
                border-right: 1px solid $table-border-color;
359
                border-right: 1px solid $table-border-color;
360
            }
360
            }
361
        }
361
        }
362
        &:nth-child(odd):not(.dtrg-group):not(.active) {
362
        &:nth-child(odd):not(.dtrg-group):not(.active):not(.ok) {
363
            td {
363
            td {
364
                &:not(.bg-danger):not(.bg-warning):not(.bg-info):not(.bg-success):not(.bg-primary) {
364
                &:not(.bg-danger):not(.bg-warning):not(.bg-info):not(.bg-success):not(.bg-primary) {
365
                    background-color: $table-odd-row;
365
                    background-color: $table-odd-row;
(-)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 for [% item.barcode | html %]</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 / +134 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="bundle_item_outside" 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('Item type') | html %]</th>
431
                                                        <th>[% t('Barcode') | html %]</th>
432
                                                        <th>[% t('Status') | html %]</th>
433
                                                    </tr>
434
                                                </thead>
435
                                                <tbody>
436
                                                    [% FOREACH bundle_item IN item.bundle_items %]
437
                                                    <tr data-barcode="[% bundle_item.barcode | html %]">
438
                                                        <td>[% INCLUDE 'biblio-title.inc' biblio=bundle_item.biblio link = 1 %]</td>
439
                                                        <td>[% bundle_item.biblio.author | html %]</td>
440
                                                        <td>[% ItemTypes.GetDescription(bundle_item.itype) | html %]</td>
441
                                                        <td>[% bundle_item.barcode | html %]</td>
442
                                                        <td>[% INCLUDE 'item-status.inc' item=bundle_item %]</td>
443
                                                    </tr>
444
                                                    [% END %]
445
                                                </tbody>
446
                                            </table>
447
448
                                            <div class="form-group">
449
                                                <label for="verify-items-bundle-contents-barcodes">Barcodes</label>
450
                                                <textarea autocomplete="off" id="verify-items-bundle-contents-barcodes" name="verify-items-bundle-contents-barcodes" class="form-control"></textarea>
451
                                                <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>
452
                                            </div>
453
454
                                        </div>
455
                                        <div class="modal-footer">
456
                                            <input type="hidden" name="barcode" value="[% item.barcode | html %]">
457
                                            <input type="hidden" name="confirm_items_bundle_return" value="1">
458
                                            [% FOREACH inputloo IN inputloop %]
459
                                            <input type="hidden" name="ri-[% inputloo.counter | html %]" value="[% inputloo.barcode | html %]" />
460
                                            <input type="hidden" name="dd-[% inputloo.counter | html %]" value="[% inputloo.duedate | html %]" />
461
                                            <input type="hidden" name="bn-[% inputloo.counter | html %]" value="[% inputloo.borrowernumber | html %]" />
462
                                            [% END %]
463
                                            <button type="submit" class="btn btn-default"><i class="fa fa-check"></i> [% t('Confirm checkin and mark missing items as lost') | html %]</button>
464
                                            <button type="button" data-dismiss="modal" class="btn btn-default"><i class="fa fa-close"></i> [% t('Cancel') | html %]</button>
465
                                        </div>
466
                                    </form>
467
                                </div>
468
                            </div>
469
                        </div>
470
                        [% END %]
471
384
                        [% IF wrongbranch %]
472
                        [% IF wrongbranch %]
385
                            <div id="wrong-branch-modal" class="modal fade audio-alert-action block">
473
                            <div id="wrong-branch-modal" class="modal fade audio-alert-action block">
386
                                <div class="modal-dialog">
474
                                <div class="modal-dialog">
Lines 1180-1185 Link Here
1180
        [% INCLUDE 'modals/resolve_return_claim.inc' %]
1268
        [% INCLUDE 'modals/resolve_return_claim.inc' %]
1181
    [% END %]
1269
    [% END %]
1182
1270
1271
    [% INCLUDE 'modals/bundle_contents.inc' %]
1272
1183
[% MACRO jsinclude BLOCK %]
1273
[% MACRO jsinclude BLOCK %]
1184
    [% INCLUDE 'datatables.inc' %]
1274
    [% INCLUDE 'datatables.inc' %]
1185
    [% INCLUDE 'columns_settings.inc' %]
1275
    [% INCLUDE 'columns_settings.inc' %]
Lines 1364-1371 Link Here
1364
                window.open("/cgi-bin/koha/members/printslip.pl?borrowernumber=" + borrowernumber + "&amp;print=checkinslip", "printwindow");
1454
                window.open("/cgi-bin/koha/members/printslip.pl?borrowernumber=" + borrowernumber + "&amp;print=checkinslip", "printwindow");
1365
            });
1455
            });
1366
1456
1457
            // item bundles
1458
            $('#verify-items-bundle-contents-barcodes').on('input', function (ev) {
1459
                const barcodes = ev.target.value.split('\n').map(function(s) { return s.trim() });
1460
                $('#items-bundle-contents-table tr').each(function () {
1461
                    const barcode = this.getAttribute('data-barcode');
1462
                    if (barcodes.includes(barcode)) {
1463
                        this.classList.add('ok');
1464
                    } else {
1465
                        this.classList.remove('ok');
1466
                    }
1467
                })
1468
            });
1469
1470
            $('.bundle_remove').on('click', function() {
1471
                var component_itemnumber = $(this).data('itemnumber');
1472
                var host_itemnumber = $(this).data('hostnumber');
1473
                var alert = $(this).closest('div');
1474
                var unlink_item_url = "/api/v1/items/" + host_itemnumber + "/bundled_items/item/" + component_itemnumber;
1475
                $.ajax({
1476
                    type: "DELETE",
1477
                    url: unlink_item_url,
1478
                    success: function(){
1479
                        alert.remove();
1480
                    }
1481
                });
1482
            });
1483
1484
            // print modals
1485
            $('.modal.printable').on('shown.bs.modal', function() {
1486
                $('.modal-dialog', this).addClass('focused');
1487
                $('body').addClass('modalprinter');
1488
1489
                if ($(this).hasClass('autoprint')) {
1490
                    window.print();
1491
                }
1492
            }).on('hidden.bs.modal', function() {
1493
                $('.modal-dialog', this).removeClass('focused');
1494
                $('body').removeClass('modalprinter');
1495
            });
1496
1497
            $('.printModal').click(function() {
1498
                window.print();
1499
            });
1367
        });
1500
        });
1368
    </script>
1501
    </script>
1502
1369
[% END %]
1503
[% END %]
1370
1504
1371
[% INCLUDE 'intranet-bottom.inc' %]
1505
[% INCLUDE 'intranet-bottom.inc' %]
1372
- 

Return to bug 28854