Bugzilla – Attachment 125197 Details for
Bug 24454
currency formatting from JS
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 28854: Update circulation functionality for bundles
Bug-28854-Update-circulation-functionality-for-bun.patch (text/plain), 24.80 KB, created by
Martin Renvoize (ashimema)
on 2021-09-23 14:39:20 UTC
(
hide
)
Description:
Bug 28854: Update circulation functionality for bundles
Filename:
MIME Type:
Creator:
Martin Renvoize (ashimema)
Created:
2021-09-23 14:39:20 UTC
Size:
24.80 KB
patch
obsolete
>From 70337d593e2a5ac14a825c1395e6d066065b4e49 Mon Sep 17 00:00:00 2001 >From: Martin Renvoize <martin.renvoize@ptfs-europe.com> >Date: Thu, 23 Sep 2021 11:37:52 +0100 >Subject: [PATCH] Bug 28854: Update circulation functionality for bundles > >This patch updates the circulation system to account for bundle >checkins. We add a content verification step to ensure bundle content is >all present at checkin and we use this comparison to mark missing items >as lost. > >Test plan >0) Apply patches up to this point >1) Checkin an item that belongs to a bundle > * An alert should be triggered noting that the item belongs to a > bundle > * The option to remove the item from the bundle should be clear > * Click remove should result in the alert dissapearing and the item > having been removed from the bundle. >2) Checkin an item bundle > * A modal confirmation dialog should appear requesting each item > barcode be scanned > * As items are scanned they should be highlighted in yellow in the > bundle content table > * Upon submission; > * The user will be alerted to any unexpected items that were > scanned and told to put them to one side. > * The user will be alerted that any missing items in the validation > will have been marked as lost. > * The bundle item will be marked as checked in. > >https://bugs.koha-community.org/show_bug.cgi?id=24454 >--- > C4/Circulation.pm | 6 + > Koha/Item.pm | 16 ++ > circ/returns.pl | 64 +++++++- > .../prog/en/includes/item-status.inc | 84 +++++++++++ > .../en/includes/modals/bundle_contents.inc | 35 +++++ > .../admin/preferences/circulation.pref | 4 + > .../prog/en/modules/circ/returns.tt | 138 ++++++++++++++++++ > 7 files changed, 343 insertions(+), 4 deletions(-) > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/item-status.inc > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/modals/bundle_contents.inc > >diff --git a/C4/Circulation.pm b/C4/Circulation.pm >index 5da1d802587..99f3c817cd9 100644 >--- a/C4/Circulation.pm >+++ b/C4/Circulation.pm >@@ -2308,6 +2308,12 @@ sub AddReturn { > } > } > >+ # Check for bundle status >+ if ( $item->in_bundle ) { >+ my $host = $item->bundle_host; >+ $messages->{InBundle} = $host; >+ } >+ > my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX }); > $indexer->index_records( $item->biblionumber, "specialUpdate", "biblioserver" ); > >diff --git a/Koha/Item.pm b/Koha/Item.pm >index 2a69ab13d00..e7ca0397220 100644 >--- a/Koha/Item.pm >+++ b/Koha/Item.pm >@@ -395,6 +395,22 @@ sub checkout { > return Koha::Checkout->_new_from_dbic( $checkout_rs ); > } > >+=head3 last_checkout >+ >+ my $old_checkout = $item->last_checkout; >+ >+Return the last_checkout for this item >+ >+=cut >+ >+sub last_checkout { >+ my ( $self ) = @_; >+ my $checkout_rs = $self->_result->old_issues->search( {}, >+ { order_by => { '-desc' => 'returndate' }, rows => 1 } )->single; >+ return unless $checkout_rs; >+ return Koha::Old::Checkout->_new_from_dbic( $checkout_rs ); >+} >+ > =head3 holds > > my $holds = $item->holds(); >diff --git a/circ/returns.pl b/circ/returns.pl >index 914d5de35c2..1e45c50ae59 100755 >--- a/circ/returns.pl >+++ b/circ/returns.pl >@@ -38,7 +38,6 @@ use C4::Auth qw( get_template_and_user get_session haspermission ); > use C4::Output qw( output_html_with_http_headers ); > use C4::Circulation qw( barcodedecode GetBranchItemRule AddReturn updateWrongTransfer LostItem ); > use C4::Reserves qw( ModReserve ModReserveAffect GetOtherReserves ); >-use C4::Circulation qw( barcodedecode GetBranchItemRule AddReturn updateWrongTransfer LostItem ); > use C4::Context; > use C4::Items qw( ModItemTransfer ); > use C4::Members::Messaging; >@@ -301,10 +300,18 @@ if ($barcode) { > $template->param( 'multiple_confirmed' => 1 ) > if $query->param('multiple_confirm'); > >+ # Block return if bundle and confirm has not been received >+ my $bundle_confirm = >+ $item >+ && $item->is_bundle >+ && !$query->param('confirm_items_bundle_return'); >+ $template->param( 'confirm_items_bundle_returned' => 1 ) >+ if $query->param('confirm_items_bundle_return'); >+ > # do the return > ( $returned, $messages, $issue, $borrower ) = > AddReturn( $barcode, $userenv_branch, $exemptfine, $return_date ) >- unless $needs_confirm; >+ unless ( $needs_confirm || $bundle_confirm ); > > if ($returned) { > my $time_now = dt_from_string()->truncate( to => 'minute'); >@@ -345,7 +352,46 @@ if ($barcode) { > ); > } > } >- } elsif ( C4::Context->preference('ShowAllCheckins') and !$messages->{'BadBarcode'} and !$needs_confirm ) { >+ >+ # Mark missing bundle items as lost and report unexpected items >+ if ( $item->is_bundle ) { >+ my $BundleLostValue = C4::Context->preference('BundleLostValue'); >+ my $checkout = $item->last_checkout; >+ my $barcodes = $query->param('verify-items-bundle-contents-barcodes'); >+ my @barcodes = map { s/^\s+|\s+$//gr } ( split /\n/, $barcodes ); >+ my $expected_items = { map { $_->barcode => $_ } $item->bundle_items->as_list }; >+ my $verify_items = Koha::Items->search( { barcode => { 'in' => \@barcodes } } ); >+ my @unexpected_items; >+ my @missing_items; >+ my @bundle_items; >+ while ( my $verify_item = $verify_items->next ) { >+ # Fix and lost statuses >+ $verify_item->itemlost(0); >+ >+ # Expected item, remove from lookup table >+ if ( delete $expected_items->{$verify_item->barcode} ) { >+ push @bundle_items, $verify_item; >+ } >+ # Unexpected item, warn and remove from bundle >+ else { >+ $verify_item->remove_from_bundle; >+ push @unexpected_items, $verify_item; >+ } >+ # Store results >+ $verify_item->store(); >+ } >+ for my $missing_item ( keys %{$expected_items} ) { >+ my $bundle_item = $expected_items->{$missing_item}; >+ $bundle_item->itemlost($BundleLostValue)->store(); >+ push @missing_items, $bundle_item; >+ } >+ $template->param( >+ unexpected_items => \@unexpected_items, >+ missing_items => \@missing_items, >+ bundle_items => \@bundle_items >+ ); >+ } >+ } elsif ( C4::Context->preference('ShowAllCheckins') and !$messages->{'BadBarcode'} and !$needs_confirm and !$bundle_confirm ) { > $input{duedate} = 0; > $returneditems{0} = $barcode; > $riduedate{0} = 0; >@@ -356,6 +402,12 @@ if ($barcode) { > if ( $needs_confirm ) { > $template->param( needs_confirm => $needs_confirm ); > } >+ >+ if ( $bundle_confirm ) { >+ $template->param( >+ items_bundle_return_confirmation => 1, >+ ); >+ } > } > $template->param( inputloop => \@inputloop ); > >@@ -553,7 +605,11 @@ foreach my $code ( keys %$messages ) { > } > elsif ( $code eq 'ReturnClaims' ) { > $template->param( ReturnClaims => $messages->{ReturnClaims} ); >- } else { >+ } >+ elsif ( $code eq 'InBundle' ) { >+ $template->param( InBundle => $messages->{InBundle} ); >+ } >+ else { > die "Unknown error code $code"; # note we need all the (empty) elsif's above, or we die. > # This forces the issue of staying in sync w/ Circulation.pm > } >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/item-status.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/item-status.inc >new file mode 100644 >index 00000000000..ca6dc1d1ff1 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/item-status.inc >@@ -0,0 +1,84 @@ >+[% USE AuthorisedValues %] >+[% USE Branches %] >+[% USE Koha %] >+[% USE KohaDates %] >+[% PROCESS 'i18n.inc' %] >+[% transfer = item.get_transfer() %] >+[% IF item.checkout %] >+ <span class="datedue"> >+ [% IF item.checkout.onsite_checkout %] >+ [% t('Currently in local use by') | html %] >+ [% ELSE %] >+ [% t('Checked out to') | html %] >+ [% END %] >+ [% INCLUDE 'patron-title.inc' patron=item.checkout.patron hide_patron_infos_if_needed=1 %] >+ : [% tx('due {date_due}', { date_due = item.checkout.date_due }) | html %] >+ </span> >+[% ELSIF transfer %] >+ [% datesent = BLOCK %][% transfer.datesent | $KohaDates %][% END %] >+ <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> >+[% END %] >+ >+[% IF item.itemlost %] >+ [% itemlost_description = AuthorisedValues.GetDescriptionByKohaField({ kohafield = 'items.itemlost', authorised_value = item.itemlost }) %] >+ [% IF itemlost_description %] >+ <span class="lost">[% itemlost_description | html %]</span> >+ [% ELSE %] >+ <span class="lost">[% t('Unavailable (lost or missing)') | html %]</span> >+ [% END %] >+[% END %] >+ >+[% IF item.withdrawn %] >+ [% withdrawn_description = AuthorisedValues.GetDescriptionByKohaField({ kohafield = 'items.withdrawn', authorised_value = item.withdrawn }) %] >+ [% IF withdrawn_description %] >+ <span class="wdn">[% withdrawn_description | html %]</span> >+ [% ELSE %] >+ <span class="wdn">[% t('Withdrawn') | html %]</span> >+ [% END %] >+[% END %] >+ >+[% IF item.damaged %] >+ [% damaged_description = AuthorisedValues.GetDescriptionByKohaField({ kohafield = 'items.damaged', authorised_value = item.damaged }) %] >+ [% IF damaged_description %] >+ <span class="dmg">[% damaged_description | html %]</span> >+ [% ELSE %] >+ <span class="dmg">[% t('Damaged') | html %]</span> >+ [% END %] >+[% END %] >+ >+[% IF item.notforloan || item.effective_itemtype.notforloan %] >+ <span> >+ [% t('Not for loan') | html %] >+ [% notforloan_description = AuthorisedValues.GetDescriptionByKohaField({ kohafield = 'items.notforloan', authorised_value = item.notforloan }) %] >+ [% IF notforloan_description %] >+ ([% notforloan_description | html %]) >+ [% END %] >+ </span> >+[% END %] >+ >+[% hold = item.holds.next %] >+[% IF hold %] >+ <span> >+ [% IF hold.waitingdate %] >+ Waiting at [% Branches.GetName(hold.get_column('branchcode')) | html %] since [% hold.waitingdate | $KohaDates %]. >+ [% ELSE %] >+ Item-level hold (placed [% hold.reservedate | $KohaDates %]) for delivery at [% Branches.GetName(hold.get_column('branchcode')) | html %]. >+ [% END %] >+ [% IF Koha.Preference('canreservefromotherbranches') %] >+ [% t('Hold for:') | html %] >+ [% INCLUDE 'patron-title.inc' patron=hold.borrower hide_patron_infos_if_needed=1 %] >+ [% END %] >+ </span> >+[% END %] >+[% UNLESS item.notforloan || item.effective_itemtype.notforloan || item.onloan || item.itemlost || item.withdrawn || item.damaged || transfer || hold %] >+ <span>[% t('Available') | html %]</span> >+[% END %] >+ >+[% IF ( item.restricted ) %] >+ [% restricted_description = AuthorisedValues.GetDescriptionByKohaField({ kohafield = 'items.restricted', authorised_value = item.restricted }) %] >+ [% IF restricted_description %] >+ <span class="restricted">([% restricted_description | html %])</span> >+ [% ELSE %] >+ <span class="restricted">[% t('Restricted') | html %]</span> >+ [% END %] >+[% END %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/modals/bundle_contents.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/modals/bundle_contents.inc >new file mode 100644 >index 00000000000..c88822b4e98 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/modals/bundle_contents.inc >@@ -0,0 +1,35 @@ >+<!-- Bundle contents modal --> >+<div class="modal printable" id="bundleContentsModal" tabindex="-1" role="dialog" aria-labelledby="bundleContentsLabel"> >+ <div class="modal-dialog" role="document"> >+ <div class="modal-content"> >+ <div class="modal-header"> >+ <button type="button" class="closebtn" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> >+ <h4 class="modal-title" id="bundleContentsLabel">Bundle contents</h4> >+ </div> >+ <div class="modal-body"> >+ <table style="width:100%"> >+ <thead> >+ <tr> >+ <th>Barcode</th> >+ <th>Title</th> >+ </tr> >+ </thead> >+ <tbody> >+ [% FOREACH bundle_item IN bundle_items %] >+ <tr> >+ <td>[% bundle_item.barcode | html %]</td> >+ <td>[% INCLUDE 'biblio-title.inc' biblio=bundle_item.biblio %]</td> >+ </tr> >+ [% END %] >+ </tbody> >+ <tfoot> >+ </tfoot> >+ </table> >+ </div> <!-- /.modal-body --> >+ <div class="modal-footer"> >+ <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> >+ <button type="button" class="printModal btn btn-primary"><i class="fa fa-print"></i> Print</button> >+ </div> <!-- /.modal-footer --> >+ </div> <!-- /.modal-content --> >+ </div> <!-- /.modal-dialog --> >+</div> <!-- /#bundleContentsModal --> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref >index fc07ec1ed65..21b4349d0f0 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref >@@ -12,6 +12,10 @@ Circulation: > 1: "Require" > 0: "Don't require" > - staff to confirm that all parts of an item are present at checkin/checkout. >+ - >+ - Use the LOST authorised value >+ - pref: BundleLostValue >+ - to represent 'missing from bundle' at return. > - > - pref: AutoSwitchPatron > choices: >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt >index 27e5c305da8..5be9b379603 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt >@@ -6,6 +6,7 @@ > [% USE ItemTypes %] > [% USE AuthorisedValues %] > [% USE TablesSettings %] >+[% PROCESS 'i18n.inc' %] > [% PROCESS 'member-display-address-style.inc' %] > [% SET footerjs = 1 %] > [% BLOCK display_bormessagepref %] >@@ -217,6 +218,36 @@ > </div> > [% END %] > >+ <!-- Bundle has items missing --> >+ [% IF missing_items %] >+ <div id="bundle_missing_items" class="dialog alert"> >+ <h3>Bundle had missing items</h3> >+ <p>Print new contents list >+ <a class="btn btn-default btn-xs" role="button" data-toggle="modal" href="#bundleContentsModal"><i class="fa fa-glass" aria-hidden="true"></i> Show</a></p> >+ </div> >+ [% END %] >+ >+ <!-- Bundle contained unexpected items --> >+ [% IF unexpected_items %] >+ <div id="bundle_unexpected_items" class="dialog alert"> >+ <h3>Bundle had unexpected items</h3> >+ <p>Please place the following items to one side</p> >+ <ul> >+ [% FOREACH unexpected_item IN unexpected_items %] >+ <li>[% INCLUDE 'biblio-title.inc' biblio=unexpected_item.biblio %] - [% unexpected_item.barcode | html %]</li> >+ [% END %] >+ </ul> >+ </div> >+ [% END %] >+ >+ <!-- Item checked in outside of bundle --> >+ [% IF InBundle %] >+ <div id="" class="dialog alert audio-alert-warning"> >+ <h3>Item belongs in bundle</h3> >+ <p>This item belongs to a bundle: [% INCLUDE 'biblio-title.inc' biblio=InBundle.biblio %] - [% InBundle.barcode | html %]</p> >+ <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> >+ </div> >+ [% END %] > > [% IF ( errmsgloop ) %] > <div class="dialog alert audio-alert-warning"> >@@ -381,6 +412,67 @@ > </div> > [% END %] > >+ [% IF items_bundle_return_confirmation %] >+ <div id="bundle-needsconfirmation-modal" class="modal fade audio-alert-action block"> >+ <div class="modal-dialog modal-wide"> >+ <div class="modal-content"> >+ <form method="post"> >+ <div class="modal-header"> >+ <h3>Please confirm bundle contents</h3> >+ </div> >+ <div class="modal-body"> >+ >+ <table class="table table-condensed table-bordered" id="items-bundle-contents-table"> >+ <thead> >+ <tr> >+ <th>[% t('Title') | html %]</th> >+ <th>[% t('Author') | html %]</th> >+ <th>[% t('Collection code') | html %]</th> >+ <th>[% t('Item type') | html %]</th> >+ <th>[% t('Callnumber') | html %]</th> >+ <th>[% t('Barcode') | html %]</th> >+ <th>[% t('Status') | html %]</th> >+ </tr> >+ </thead> >+ <tbody> >+ [% FOREACH bundle_item IN item.bundle_items %] >+ <tr data-barcode="[% bundle_item.barcode | html %]"> >+ <td>[% INCLUDE 'biblio-title.inc' biblio=bundle_item.biblio link = 1 %]</td> >+ <td>[% bundle_item.biblio.author | html %]</td> >+ <td>[% AuthorisedValues.GetByCode('CCODE', bundle_item.ccode) | html %]</td> >+ <td>[% ItemTypes.GetDescription(bundle_item.itype) | html %]</td> >+ <td>[% bundle_item.itemcallnumber | html %]</td> >+ <td>[% bundle_item.barcode | html %]</td> >+ <td>[% INCLUDE 'item-status.inc' item=bundle_item %]</td> >+ </tr> >+ [% END %] >+ </tbody> >+ </table> >+ >+ <div class="form-group"> >+ <label for="verify-items-bundle-contents-barcodes">Barcodes</label> >+ <textarea autocomplete="off" id="verify-items-bundle-contents-barcodes" name="verify-items-bundle-contents-barcodes" class="form-control"></textarea> >+ <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> >+ </div> >+ >+ </div> >+ <div class="modal-footer"> >+ <input type="hidden" name="barcode" value="[% item.barcode | html %]"> >+ <input type="hidden" name="confirm_items_bundle_return" value="1"> >+ [% FOREACH inputloo IN inputloop %] >+ <input type="hidden" name="ri-[% inputloo.counter | html %]" value="[% inputloo.barcode | html %]" /> >+ <input type="hidden" name="dd-[% inputloo.counter | html %]" value="[% inputloo.duedate | html %]" /> >+ <input type="hidden" name="bn-[% inputloo.counter | html %]" value="[% inputloo.borrowernumber | html %]" /> >+ [% END %] >+ <button type="submit" class="btn btn-default"><i class="fa fa-check"></i> [% t('Confirm checkin and mark missing items as lost') | html %]</button> >+ <button type="button" data-dismiss="modal" class="btn btn-default"><i class="fa fa-close"></i> [% t('Cancel') | html %]</button> >+ </div> >+ </form> >+ </div> >+ </div> >+ </div> >+ [% END %] >+ > [% IF wrongbranch %] > <div id="wrong-branch-modal" class="modal fade audio-alert-action block"> > <div class="modal-dialog"> >@@ -986,6 +1078,8 @@ > </div> <!-- /.col-sm-12 --> > </div> <!-- /.row --> > >+ [% INCLUDE 'modals/bundle_contents.inc' %] >+ > [% MACRO jsinclude BLOCK %] > [% INCLUDE 'datatables.inc' %] > [% INCLUDE 'columns_settings.inc' %] >@@ -1188,8 +1282,52 @@ > window.open("/cgi-bin/koha/members/printslip.pl?borrowernumber=" + borrowernumber + "&print=checkinslip", "printwindow"); > }); > >+ // item bundles >+ $('#verify-items-bundle-contents-barcodes').on('input', function (ev) { >+ const barcodes = ev.target.value.split('\n').map(function(s) { return s.trim() }); >+ $('#items-bundle-contents-table tr').each(function () { >+ const barcode = this.getAttribute('data-barcode'); >+ if (barcodes.includes(barcode)) { >+ this.classList.add('ok'); >+ } else { >+ this.classList.remove('ok'); >+ } >+ }) >+ }); >+ >+ $('.bundle_remove').on('click', function() { >+ var component_itemnumber = $(this).data('itemnumber'); >+ var host_itemnumber = $(this).data('hostnumber'); >+ var alert = $(this).closest('div'); >+ var unlink_item_url = "/api/v1/items/" + host_itemnumber + "/bundled_items/item/" + component_itemnumber; >+ $.ajax({ >+ type: "DELETE", >+ url: unlink_item_url, >+ success: function(){ >+ alert.remove(); >+ } >+ }); >+ }); >+ >+ // print modals >+ $('.modal.printable').on('shown.bs.modal', function() { >+ $('.modal-dialog', this).addClass('focused'); >+ $('body').addClass('modalprinter'); >+ >+ if ($(this).hasClass('autoprint')) { >+ window.print(); >+ } >+ }).on('hidden.bs.modal', function() { >+ $('.modal-dialog', this).removeClass('focused'); >+ $('body').removeClass('modalprinter'); >+ }); >+ >+ $('.printModal').click(function() { >+ window.print(); >+ }); > }); > </script> >+ > [% END %] > > [% INCLUDE 'intranet-bottom.inc' %] >-- >2.20.1
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 24454
:
97573
|
98228
|
125193
|
125194
|
125195
|
125196
|
125197
|
125198
|
125199