Bugzilla – Attachment 100253 Details for
Bug 23112
Add circulation process to inter-library loans
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 23112: Add circulation to ILL requests
Bug-23112-Add-circulation-to-ILL-requests.patch (text/plain), 27.26 KB, created by
Nick Clemens (kidclamp)
on 2020-03-06 11:37:11 UTC
(
hide
)
Description:
Bug 23112: Add circulation to ILL requests
Filename:
MIME Type:
Creator:
Nick Clemens (kidclamp)
Created:
2020-03-06 11:37:11 UTC
Size:
27.26 KB
patch
obsolete
>From 4ddfeb1d971471e07dcd23328c9c6f03f3ec52e3 Mon Sep 17 00:00:00 2001 >From: Andrew Isherwood <andrew.isherwood@ptfs-europe.com> >Date: Thu, 25 Jul 2019 13:46:12 +0100 >Subject: [PATCH] Bug 23112: Add circulation to ILL requests > >This patch adds the ability to circulate ILL requests. Once a request has a suitable status, a "Check out" button is displayed on the "Manage request" toolbar. Clicking this will enable the user to check out the item either to the user who made the request or an in-house statistical user. A due date can be specified, but if not circ rules are used. > >Prior to the check out, an item is created which is attached to the biblio record that was created when the request was added > >This development has been carried out according to the originally stated requirements of the customer that sponsored it, detailed here: https://wiki.koha-community.org/wiki/ILL_Circulation_RFC > >Test plan: > >1. Ensure the FreeForm ILL backend is available >2. Enable the "CirculateILL" syspref >3. Ensure you have a statistical patron category defined (patron category type "Statistical") >4. Ensure you have at least one patron in your statistical patron category >5. Create a new FreeForm request (make a note of the library you select when creating it) >6. Mark the new request as confirmed by clicking the "Confirm request" button on the "Manage ILL request" page >7. TEST: Observe that a "Check out" button is now displayed in the request toolbar >8. Click the "Check out" button in the "Manage ILL request" page >9. In the "Issue requested item to..." screen: > a. Do not select a statistical patron at this time > b. You can at this point choose an item type, this will determine the type of the item that will be created for this request > c. TEST: Observe that the default selected "Library" matches that that was defined when creating the request > d. Do not select a due date at this time >10. Click "Submit" >11. TEST: Observe that the "Item checked out" screen displays, issued to the requesting patron with a due date corresponding to appropriate circ rules >12. Click "Return to request" >13. TEST: Observe that the request's status is now "Checked out" >14. Click the "Bibliographic record ID" link >15. TEST: Observe that the bibliographic record now has one item attached to it which is checked out >16. TEST: Observe that the item barcode is "ILL-" + the ILL request ID >17. Return to step 5., however, this time select a statistical patron and test that the item use is recorded and the item is not issued >18. Return to step 5., however, this time manually select a due date and test that the item's due date is set correctly on check out >19. Check in the item >20. TEST: Observe that the request's status is updated to "Returned to library" >21. Now implement a restriction on the patron (perhaps a fine) which would prevent them from checking out an item >22. Return to step 5. follow the instructions to step 10. >23. TEST: Observe that a banner is displayed at the top of the screen informing you that there was a problem checking the item out, containing a link to the patron's account page >24. Resolve the problem with the patron's account >25. Return to step 8. >26. TEST: Observe that the item is now successfully checked out >27. Disable the "CirculateILL" syspref >28. Return to step 5. at step 7. Observe that the "Check out" button is NOT displayed > >Sponsored-by: Loughborough University > >Signed-off-by: Nick Clemens <nick@bywatersolutions.com> >--- > Koha/Illrequest.pm | 222 ++++++++++++++++++++- > ill/ill-requests.pl | 8 + > .../intranet-tmpl/prog/css/src/staff-global.scss | 4 + > .../prog/en/modules/ill/ill-requests.tt | 163 +++++++++++++++ > 4 files changed, 394 insertions(+), 3 deletions(-) > >diff --git a/Koha/Illrequest.pm b/Koha/Illrequest.pm >index d7a16dea4d..519fadb638 100644 >--- a/Koha/Illrequest.pm >+++ b/Koha/Illrequest.pm >@@ -35,6 +35,12 @@ use Koha::AuthorisedValue; > use Koha::Illrequest::Logger; > use Koha::Patron; > use Koha::AuthorisedValues; >+use Koha::Biblios; >+use Koha::Items; >+use Koha::ItemTypes; >+use Koha::Libraries; >+use C4::Items qw( AddItem ); >+use C4::Circulation qw( CanBookBeIssued AddIssue ); > > use base qw(Koha::Object); > >@@ -425,7 +431,7 @@ sub _core_status_graph { > name => 'Requested', > ui_method_name => 'Confirm request', > method => 'confirm', >- next_actions => [ 'REQREV', 'COMP' ], >+ next_actions => [ 'REQREV', 'COMP', 'CHK' ], > ui_method_icon => 'fa-check', > }, > GENREQ => { >@@ -434,7 +440,7 @@ sub _core_status_graph { > name => 'Requested from partners', > ui_method_name => 'Place request with partners', > method => 'generic_confirm', >- next_actions => [ 'COMP' ], >+ next_actions => [ 'COMP', 'CHK' ], > ui_method_icon => 'fa-send-o', > }, > REQREV => { >@@ -470,7 +476,7 @@ sub _core_status_graph { > name => 'Completed', > ui_method_name => 'Mark completed', > method => 'mark_completed', >- next_actions => [ ], >+ next_actions => [ 'CHK' ], > ui_method_icon => 'fa-check', > }, > KILL => { >@@ -482,6 +488,15 @@ sub _core_status_graph { > next_actions => [ ], > ui_method_icon => 'fa-trash', > }, >+ CHK => { >+ prev_actions => [ 'REQ', 'GENREQ', 'COMP' ], >+ id => 'CHK', >+ name => 'Checked out', >+ ui_method_name => 'Check out', >+ method => 'check_out', >+ next_actions => [ ], >+ ui_method_icon => 'fa-upload', >+ } > }; > } > >@@ -1023,6 +1038,207 @@ sub requires_moderation { > return $require_moderation->{$self->status}; > } > >+=head3 check_out >+ >+ my $stage_summary = $request->check_out; >+ >+Handle the check_out method. The first stage involves gathering the required >+data from the user via a form, the second stage creates an item and tries to >+issue it to the patron. If successful, it notifies the patron, then it >+returns a summary of how things went >+ >+=cut >+ >+sub check_out { >+ my ( $self, $params ) = @_; >+ >+ # Objects required by the template >+ my $itemtypes = Koha::ItemTypes->search( >+ {}, >+ { order_by => ['description'] } >+ ); >+ my $libraries = Koha::Libraries->search( >+ {}, >+ { order_by => ['branchcode'] } >+ ); >+ my $biblio = Koha::Biblios->find({ >+ biblionumber => $self->biblio_id >+ }); >+ # Find all statistical patrons >+ my $statistical_patrons = Koha::Patrons->search( >+ { 'category_type' => 'x' }, >+ { join => { 'categorycode' => 'borrowers' } } >+ ); >+ >+ if (!$params->{stage} || $params->{stage} eq 'init') { >+ # Present a form to gather the required data >+ # >+ # We may be viewing this page having previously tried to issue >+ # the item (in which case, we may already have created an item) >+ # so we pass the biblio for this request >+ return { >+ method => 'check_out', >+ stage => 'form', >+ value => { >+ itemtypes => $itemtypes, >+ libraries => $libraries, >+ statistical => $statistical_patrons, >+ biblio => $biblio >+ } >+ }; >+ } elsif ($params->{stage} eq 'form') { >+ # Validate what we've got and return with an error if we fail >+ my $errors = {}; >+ if (!$params->{item_type} || length $params->{item_type} == 0) { >+ $errors->{item_type} = 1; >+ } >+ if ($params->{inhouse} && length $params->{inhouse} > 0) { >+ my $patron_count = Koha::Patrons->search({ >+ cardnumber => $params->{inhouse} >+ })->count(); >+ if ($patron_count != 1) { >+ $errors->{inhouse} = 1; >+ } >+ } >+ >+ # Check we don't have more than one item for this bib, >+ # if we do, something very odd is going on >+ # Having 1 is OK, it means we're likely trying to issue >+ # following a previously failed attempt, the item exists >+ # so we'll use it >+ my @items = $biblio->items->as_list; >+ my $item_count = scalar @items; >+ if ($item_count > 1) { >+ $errors->{itemcount} = 1; >+ } >+ >+ # Failed validation, go back to the form >+ if (%{$errors}) { >+ return { >+ method => 'check_out', >+ stage => 'form', >+ value => { >+ params => $params, >+ statistical => $statistical_patrons, >+ itemtypes => $itemtypes, >+ libraries => $libraries, >+ biblio => $biblio, >+ errors => $errors >+ } >+ }; >+ } >+ >+ # Passed validation >+ # >+ # Create an item if one doesn't already exist, >+ # if one does, use that >+ my $itemnumber; >+ if ($item_count == 0) { >+ my $item_hash = { >+ homebranch => $params->{branchcode}, >+ holdingbranch => $params->{branchcode}, >+ location => $params->{branchcode}, >+ itype => $params->{item_type}, >+ barcode => 'ILL-' . $self->illrequest_id >+ }; >+ my (undef, undef, $item_no) = >+ AddItem($item_hash, $self->biblio_id); >+ $itemnumber = $item_no; >+ } else { >+ $itemnumber = $items[0]->itemnumber; >+ } >+ # Check we have an item before going forward >+ if (!$itemnumber) { >+ return { >+ method => 'check_out', >+ stage => 'form', >+ value => { >+ params => $params, >+ itemtypes => $itemtypes, >+ libraries => $libraries, >+ statistical => $statistical_patrons, >+ errors => { item_creation => 1 } >+ } >+ }; >+ } >+ >+ # Do the check out >+ # >+ # Gather what we need >+ my $target_item = Koha::Items->find( $itemnumber ); >+ # Determine who we're issuing to >+ my $patron = $params->{inhouse} && length $params->{inhouse} > 0 ? >+ Koha::Patrons->find({ cardnumber => $params->{inhouse} }) : >+ $self->patron; >+ >+ my @issue_args = ( >+ $patron, >+ scalar $target_item->barcode >+ ); >+ if ($params->{duedate} && length $params->{duedate} > 0) { >+ push @issue_args, $params->{duedate}; >+ } >+ # Check if we can check out >+ my ( $error, $confirm, $alerts, $messages ) = >+ C4::Circulation::CanBookBeIssued(@issue_args); >+ >+ # If we got anything back saying we can't check out, >+ # return it to the template >+ my $problems = {}; >+ if ( $error && %{$error} ) { $problems->{error} = $error }; >+ if ( $confirm && %{$confirm} ) { $problems->{confirm} = $confirm }; >+ if ( $alerts && %{$alerts} ) { $problems->{alerts} = $alerts }; >+ if ( $messages && %{$messages} ) { $problems->{messages} = $messages }; >+ >+ if (%{$problems}) { >+ return { >+ method => 'check_out', >+ stage => 'form', >+ value => { >+ params => $params, >+ itemtypes => $itemtypes, >+ libraries => $libraries, >+ statistical => $statistical_patrons, >+ patron => $patron, >+ biblio => $biblio, >+ check_out_errors => $problems >+ } >+ }; >+ } >+ >+ # We can allegedly check out, so make it so >+ # For some reason, AddIssue requires an unblessed Patron >+ $issue_args[0] = $patron->unblessed; >+ my $issue = C4::Circulation::AddIssue(@issue_args); >+ >+ if ($issue && %{$issue}) { >+ # Update the request status >+ $self->status('CHK')->store; >+ return { >+ method => 'check_out', >+ stage => 'done_check_out', >+ value => { >+ params => $params, >+ patron => $patron, >+ check_out => $issue >+ } >+ }; >+ } else { >+ return { >+ method => 'check_out', >+ stage => 'form', >+ value => { >+ params => $params, >+ itemtypes => $itemtypes, >+ libraries => $libraries, >+ errors => { item_check_out => 1 } >+ } >+ }; >+ } >+ } >+ >+} >+ > =head3 generic_confirm > > my $stage_summary = $illRequest->generic_confirm; >diff --git a/ill/ill-requests.pl b/ill/ill-requests.pl >index 3a256c3515..f31577eea9 100755 >--- a/ill/ill-requests.pl >+++ b/ill/ill-requests.pl >@@ -267,6 +267,14 @@ if ( $backends_available ) { > > # handle special commit rules & update type > handle_commit_maybe($backend_result, $request); >+ } elsif ( $op eq 'check_out') { >+ my $request = Koha::Illrequests->find($params->{illrequest_id}); >+ my $backend_result = $request->check_out($params); >+ $template->param( >+ params => $params, >+ whole => $backend_result, >+ request => $request >+ ); > } elsif ( $op eq 'illlist') { > > # If we receive a pre-filter, make it available to the template >diff --git a/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss b/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss >index 6a4f1e6c3f..7e4cdb62d1 100644 >--- a/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss >+++ b/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss >@@ -3784,6 +3784,10 @@ input.renew { > } > } > >+#ill-issue-title { >+ margin: 20px 0 30px 0; >+} >+ > #stockrotation { > h3 { > margin: 30px 0 10px 0; >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt >index a39b93c235..bda27e71b5 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt >@@ -111,6 +111,152 @@ > <h1>Cancel a confirmed request</h1> > [% PROCESS $whole.template %] > >+ [% ELSIF query_type == 'check_out' and !whole.error %] >+ [% IF !whole.stage || whole.stage == 'form' %] >+ <h1 id="ill-issue-title">Issue requested item to [% request.patron.firstname %] [% request.patron.surname %]</h1> >+ [% IF !request.biblio_id || request.biblio_id.length == 0 %] >+ <div class="alert">This item cannot be issued as it has no biblio record associated with it</div> >+ [% END %] >+ [% IF whole.value.errors.itemcount %] >+ <div class="alert">The bibliographic record for this request has multiple items, it should only have one. Please fix this then try again.</div> >+ [% END %] >+ [% IF whole.value.errors.item_creation %] >+ <div class="alert">An unknown error occurred while trying to add an item</div> >+ [% END %] >+ [% IF whole.value.errors.item_check_out %] >+ <div class="alert">An unknown error occurred while trying to check out the item</div> >+ [% END %] >+ [% IF whole.value.check_out_errors %] >+ [% IF whole.value.check_out_errors.error.STATS %] >+ <div class="alert"> >+ Local use recorded >+ </div> >+ [% ELSE %] >+ <div class="alert"> >+ There was a problem checking this item out, please check for problems with the <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% whole.value.patron.borrowernumber %]">patron's account</a> >+ </div> >+ [% END %] >+ [% END %] >+ [% IF request.biblio_id && request.biblio_id.length > 0 && !whole.value.check_out_errors.error.STATS %] >+ <form method="POST" action="/cgi-bin/koha/ill/ill-requests.pl"> >+ <fieldset class="rows"> >+ <legend>Check out details</legend> >+ [% items = whole.value.biblio.items.unblessed %] >+ [% IF items.size == 1 %] >+ <p>The bibliographic record for this request already has an item attached to it, you are about to check it out</p> >+ [% ELSE %] >+ <p>A bibliographic record for this request exists, but no item. You are about to create an item and check it out</p> >+ [% END %] >+ <ol> >+ <li class="ill_checkout_inhouse"> >+ <label for="inhouse" class="ill_checkout_inhouse_label">Statistical patron:</label> >+ <select id="ill_checkout_inhouse_select" name="inhouse" class="ill_checkout_inhouse_select"> >+ <option value=""></option> >+ [% FOREACH stat IN whole.value.statistical %] >+ [% IF stat.borrowernumber == params.inhouse %] >+ <option value="[% stat.cardnumber %]" selected>[% stat.firstname %] [% stat.surname %]</option> >+ [% ELSE %] >+ <option value="[% stat.cardnumber %]">[% stat.firstname %] [% stat.surname %]</option> >+ [% END %] >+ [% END %] >+ </select> >+ [% IF whole.value.errors.inhouse %] >+ <span class="required">You must choose a valid patron</span> >+ [% END %] >+ <div class="hint">If you do not wish to check out the item to [% request.patron.firstname | html %] [% request.patron.surname | html %] and would rather issue it to an in-house statistical patron, choose the patron here</div> >+ </li> >+ <li class="ill_checkout_item_type"> >+ <label for="item_type" class="ill_checkout_item_type_label required">Item type:</label> >+ [% IF items.size != 1 %] >+ <select id="ill_checkout_item_type_select" name="item_type" required> >+ [% FOREACH type IN whole.value.itemtypes %] >+ [% IF type.itemtype == params.item_type %] >+ <option value="[% type.itemtype | html %]" selected> >+ [% ELSE %] >+ <option value="[% type.itemtype | html %]"> >+ [% END %] >+ [% type.description | html %] >+ </option> >+ [% END %] >+ </select> >+ [% ELSE %] >+ [% FOREACH type IN whole.value.itemtypes %] >+ [% IF type.itemtype == items.0.itype %] >+ [% type.description | html %] >+ [% END %] >+ [% END %] >+ [% END %] >+ [% IF whole.value.errors.item_type %] >+ <span class="required">You must choose an item type</span> >+ [% END %] >+ </li> >+ [% IF items.size == 1 %] >+ <li> >+ <label for="barcode" class="ill_checkout_barcode_label">Item barcode:</label> >+ [% items.0.barcode %] >+ </li> >+ [% END %] >+ <li class="ill_checkout_branchcode"> >+ <label for="branchcode" class="ill_checkout_branchcode_label required">Library:</label> >+ [% branchcode = items.size == 1 ? items.0.homebranch : params.branchcode ? params.branchcode : request.branchcode %] >+ [% IF items.size != 1 %] >+ <select name="branchcode" id="ill_checkout_branchcode_select" required> >+ [% PROCESS options_for_libraries libraries => Branches.all( selected => branchcode ) %] >+ </select> >+ [% ELSE %] >+ [% FOREACH branch IN whole.value.libraries.unblessed %] >+ [% IF branch.branchcode == branchcode %] >+ [% branch.branchname %] >+ [% END %] >+ [% END %] >+ [% END %] >+ [% IF whole.value.errors.branchcode %] >+ <span class="required">You must choose a branch</span> >+ [% END %] >+ </li> >+ <li class="ill_checkout_due_date"> >+ <label for="duedate" class="ill_checkout_duedate_label">Due date:</label> >+ <input name="duedate" id="ill_checkout_duedate_input" type="text" value="[% params.duedate | html %]"> [% INCLUDE 'date-format.inc' %] >+ <div class="hint">If you do not specify a due date, it will be set according to circulation rules</p> >+ </li> >+ </ol> >+ </fieldset> >+ <fieldset class="action"> >+ <input type="hidden" value="check_out" name="method"> >+ <input type="hidden" value="form" name="stage"> >+ [% IF items.size == 1 %] >+ <input name="branchcode" type="hidden" value="[% branchcode %]"> >+ <input name="item_type" type="hidden" value="[% items.0.itype %]"> >+ [% END %] >+ <input type="hidden" value="[% request.illrequest_id | html %]" name="illrequest_id"> >+ <input type="submit" value="Submit"> >+ <a class="cancel" href="/cgi-bin/koha/ill/ill-requests.pl?method=illview&illrequest_id=[% request.id | html %]">Cancel</a> >+ </fieldset> >+ </form> >+ [% END %] >+ [% IF whole.value.check_out_errors.error.STATS %] >+ <a class="cancel" href="/cgi-bin/koha/ill/ill-requests.pl?method=illview&illrequest_id=[% request.id | html %]">Return to request</a> >+ [% END %] >+ [% ELSIF whole.stage == 'done_check_out' %] >+ <h1>Item checked out</h1> >+ <fieldset class="rows"> >+ <legend>Check out details</legend> >+ <ol> >+ <li> >+ <label>Checked out to:</label> >+ [% whole.value.patron.firstname | html %] [% whole.value.patron.surname | html %] >+ </li> >+ <li> >+ <label>Due date:</label> >+ [% whole.value.check_out.date_due | $KohaDates with_hours => 1 %] >+ </li> >+ </ol> >+ </fieldset> >+ <fieldset class="action"> >+ <a class="cancel" href="/cgi-bin/koha/ill/ill-requests.pl?method=illview&illrequest_id=[% request.id | html %]">Return to request</a> >+ </fieldset> >+ [% END %] >+ > [% ELSIF query_type == 'generic_confirm' %] > <h1>Place request with partner libraries</h1> > [% IF error %] >@@ -534,14 +680,31 @@ > [% INCLUDE 'datatables.inc' %] > [% INCLUDE 'columns_settings.inc' %] > [% INCLUDE 'calendar.inc' %] >+ [% Asset.js("lib/jquery/plugins/jquery-ui-timepicker-addon.min.js") | $raw %] > [% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %] > <script> > var prefilters = '[% prefilters | $raw %]'; > // Set column settings > var columns_settings = [% ColumnsSettings.GetColumns( 'illrequests', 'ill-requests', 'ill-requests', 'json' ) %]; >+ $("#ill_checkout_duedate_input").datetimepicker({ >+ hour: 23, >+ minute: 59 >+ }).on("change", function(e, value) { >+ if ( ! is_valid_date( $(this).val() ) ) {$(this).val("");} >+ }); >+ </script> >+ <script> >+ $('#ill_checkout_inhouse_select').on('change', function() { >+ if ($(this).val().length > 0) { >+ $('.ill_checkout_due_date').hide(); >+ } else { >+ $('.ill_checkout_due_date').show(); >+ } >+ }); > </script> > [% INCLUDE 'ill-list-table-strings.inc' %] > [% Asset.js("js/ill-list-table.js") | $raw %] >+ [% Asset.js("js/ill-check-out.js") | $raw %] > [% END %] > > [% TRY %] >-- >2.11.0
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 23112
:
91987
|
91988
|
91989
|
91990
|
91991
|
92000
|
92001
|
92002
|
92003
|
92004
|
93530
|
93531
|
93532
|
93533
|
93534
|
93958
|
94264
|
94265
|
94266
|
94267
|
94268
|
94269
|
95565
|
95566
|
95567
|
95568
|
95569
|
95570
|
96834
|
96870
|
99910
|
99911
|
99912
|
99913
|
99914
|
99915
|
99916
|
99917
|
100100
|
100101
|
100102
|
100103
|
100104
|
100105
|
100106
|
100107
|
100108
|
100169
|
100224
|
100225
|
100252
| 100253 |
100254
|
100255
|
100256
|
100257
|
100258
|
100259
|
100260
|
100261
|
100262
|
100263