From 3f9c6030be8f5a5ce6e46d69a30ac099bd6421c8 Mon Sep 17 00:00:00 2001 From: Andrew Isherwood 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 Sponsored-by: Loughborough University --- 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 4f9623a9f5..2987a7af4e 100644 --- a/Koha/Illrequest.pm +++ b/Koha/Illrequest.pm @@ -36,6 +36,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); @@ -426,7 +432,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 => { @@ -435,7 +441,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 => { @@ -471,7 +477,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 => { @@ -483,6 +489,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', + } }; } @@ -1024,6 +1039,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 010c61e618..9c695dcf32 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 1f0c3274d2..29544b4f6b 100644 --- a/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss +++ b/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss @@ -3771,6 +3771,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 2fa9c02017..2a0c1b6f68 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 @@

Cancel a confirmed request

[% PROCESS $whole.template %] + [% ELSIF query_type == 'check_out' and !whole.error %] + [% IF !whole.stage || whole.stage == 'form' %] +

Issue requested item to [% request.patron.firstname %] [% request.patron.surname %]

+ [% IF !request.biblio_id || request.biblio_id.length == 0 %] +
This item cannot be issued as it has no biblio record associated with it
+ [% END %] + [% IF whole.value.errors.itemcount %] +
The bibliographic record for this request has multiple items, it should only have one. Please fix this then try again.
+ [% END %] + [% IF whole.value.errors.item_creation %] +
An unknown error occurred while trying to add an item
+ [% END %] + [% IF whole.value.errors.item_check_out %] +
An unknown error occurred while trying to check out the item
+ [% END %] + [% IF whole.value.check_out_errors %] + [% IF whole.value.check_out_errors.error.STATS %] +
+ Local use recorded +
+ [% ELSE %] +
+ There was a problem checking this item out, please check for problems with the patron's account +
+ [% END %] + [% END %] + [% IF request.biblio_id && request.biblio_id.length > 0 && !whole.value.check_out_errors.error.STATS %] +
+
+ Check out details + [% items = whole.value.biblio.items.unblessed %] + [% IF items.size == 1 %] +

The bibliographic record for this request already has an item attached to it, you are about to check it out

+ [% ELSE %] +

A bibliographic record for this request exists, but no item. You are about to create an item and check it out

+ [% END %] +
    +
  1. + + + [% IF whole.value.errors.inhouse %] + You must choose a valid patron + [% END %] +
    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
    +
  2. +
  3. + + [% IF items.size != 1 %] + + [% 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 %] + You must choose an item type + [% END %] +
  4. + [% IF items.size == 1 %] +
  5. + + [% items.0.barcode %] +
  6. + [% END %] +
  7. + + [% branchcode = items.size == 1 ? items.0.homebranch : params.branchcode ? params.branchcode : request.branchcode %] + [% IF items.size != 1 %] + + [% ELSE %] + [% FOREACH branch IN whole.value.libraries.unblessed %] + [% IF branch.branchcode == branchcode %] + [% branch.branchname %] + [% END %] + [% END %] + [% END %] + [% IF whole.value.errors.branchcode %] + You must choose a branch + [% END %] +
  8. +
  9. + + [% INCLUDE 'date-format.inc' %] +
    If you do not specify a due date, it will be set according to circulation rules

    +
  10. +
+
+
+ + + [% IF items.size == 1 %] + + + [% END %] + + + Cancel +
+
+ [% END %] + [% IF whole.value.check_out_errors.error.STATS %] + Return to request + [% END %] + [% ELSIF whole.stage == 'done_check_out' %] +

Item checked out

+
+ Check out details +
    +
  1. + + [% whole.value.patron.firstname | html %] [% whole.value.patron.surname | html %] +
  2. +
  3. + + [% whole.value.check_out.date_due | $KohaDates with_hours => 1 %] +
  4. +
+
+
+ Return to request +
+ [% END %] + [% ELSIF query_type == 'generic_confirm' %]

Place request with partner libraries

[% IF error %] @@ -534,16 +680,33 @@ [% 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 %] + [% IF query_type == 'illlist' %] [% INCLUDE 'ill-list-table-strings.inc' %] [% Asset.js("js/ill-list-table.js") | $raw %] [% END %] + [% Asset.js("js/ill-check-out.js") | $raw %] [% END %] [% TRY %] -- 2.11.0