From c0ad30cce6bca7f5b1275411c68891945608d804 Mon Sep 17 00:00:00 2001 From: Alex Sassmannshausen Date: Fri, 3 Feb 2017 16:58:35 +0100 Subject: [PATCH] Bug 7317: ILLModule * Koha/Illrequest.pm * Koha/Illrequest/Config.pm * Koha/Illrequestattribute.pm * Koha/Illrequestattributes.pm * Koha/Illrequests.pm * Koha/REST/V1/Illrequests.pm * ill/ill-requests.pl * api/v1/swagger/paths.json * api/v1/swagger/paths/illrequests.json * etc/koha-conf.xml * koha-tmpl/intranet-tmpl/prog/css/staff-global.css * koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc * koha-tmpl/intranet-tmpl/prog/en/includes/ill-toolbar.inc * koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc * koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt * koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt * koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc * koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-illrequests.tt * koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results-grouped.tt * koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results.tt * koha-tmpl/opac-tmpl/bootstrap/less/opac.less * opac/opac-illrequests.pl * t/db_dependent/Illrequest.t * t/db_dependent/Illrequest/Config.t * t/db_dependent/Illrequest/Dummy.t * t/db_dependent/api/v1/illrequests.t Signed-off-by: Magnus Enger --- Koha/Illrequest.pm | 855 +++++++++++++++++++++ Koha/Illrequest/Backend/Dummy/Base.pm | 576 ++++++++++++++ Koha/Illrequest/Config.pm | 389 ++++++++++ Koha/Illrequestattribute.pm | 51 ++ Koha/Illrequestattributes.pm | 55 ++ Koha/Illrequests.pm | 99 +++ Koha/REST/V1/Illrequests.pm | 85 ++ api/v1/swagger/paths.json | 3 + api/v1/swagger/paths/illrequests.json | 98 +++ etc/koha-conf.xml | 21 + ill/ill-requests.pl | 238 ++++++ koha-tmpl/intranet-tmpl/prog/css/staff-global.css | 77 ++ .../intranet-tmpl/prog/en/includes/circ-menu.inc | 3 + .../intranet-tmpl/prog/en/includes/ill-toolbar.inc | 24 + .../intranet-tmpl/prog/en/includes/permissions.inc | 1 + .../prog/en/modules/ill/ill-requests.tt | 487 ++++++++++++ .../intranet-tmpl/prog/en/modules/intranet-main.tt | 5 + .../opac-tmpl/bootstrap/en/includes/usermenu.inc | 9 + .../bootstrap/en/modules/opac-illrequests.tt | 219 ++++++ .../bootstrap/en/modules/opac-results-grouped.tt | 30 +- .../opac-tmpl/bootstrap/en/modules/opac-results.tt | 33 +- koha-tmpl/opac-tmpl/bootstrap/less/opac.less | 38 + opac/opac-illrequests.pl | 129 ++++ t/db_dependent/Illrequest.t | 544 +++++++++++++ t/db_dependent/Illrequest/Config.t | 344 +++++++++ t/db_dependent/Illrequest/Dummy.t | 378 +++++++++ t/db_dependent/api/v1/illrequests.t | 136 ++++ 27 files changed, 4910 insertions(+), 17 deletions(-) create mode 100644 Koha/Illrequest.pm create mode 100644 Koha/Illrequest/Backend/Dummy/Base.pm create mode 100644 Koha/Illrequest/Config.pm create mode 100644 Koha/Illrequestattribute.pm create mode 100644 Koha/Illrequestattributes.pm create mode 100644 Koha/Illrequests.pm create mode 100644 Koha/REST/V1/Illrequests.pm create mode 100644 api/v1/swagger/paths/illrequests.json create mode 100755 ill/ill-requests.pl create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/ill-toolbar.inc create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt create mode 100644 koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-illrequests.tt create mode 100755 opac/opac-illrequests.pl create mode 100644 t/db_dependent/Illrequest.t create mode 100644 t/db_dependent/Illrequest/Config.t create mode 100644 t/db_dependent/Illrequest/Dummy.t create mode 100644 t/db_dependent/api/v1/illrequests.t diff --git a/Koha/Illrequest.pm b/Koha/Illrequest.pm new file mode 100644 index 0000000..d975ede --- /dev/null +++ b/Koha/Illrequest.pm @@ -0,0 +1,855 @@ +package Koha::Illrequest; + +# Copyright PTFS Europe 2016 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# Koha; if not, write to the Free Software Foundation, Inc., 51 Franklin +# Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# use Modern::Perl; + +use Clone 'clone'; +use File::Basename qw/basename/; +use Koha::Database; +use Koha::Email; +use Koha::Illrequestattributes; +use Koha::Patron; +use Mail::Sendmail; + +use base qw(Koha::Object); + +=head1 NAME + +Koha::Illrequest - Koha Illrequest Object class + +=head1 (Re)Design + +An ILLRequest consists of two parts; the Illrequest Koha::Object, and a series +of related Illrequestattributes. + +The former encapsulates the basic necessary information that any ILL requires +to be usable in Koha. The latter is a set of additional properties used by +one of the backends. + +The former subsumes the legacy "Status" object. The latter remains +encapsulated in the "Record" object. + +TODO: + +- Anything invoking the ->status method; annotated with: + + # Old use of ->status ! + +=head1 API + +=head2 Backend API Response Principles + +All methods should return a hashref in the following format: + +=item * error + +This should be set to 1 if an error was encountered. + +=item * status + +The status should be a string from the list of statuses detailed below. + +=item * message + +The message is a free text field that can be passed on to the end user. + +=item * value + +The value returned by the method. + +=over + +=head2 Interface Status Messages + +=over + +=item * branch_address_incomplete + +An interface request has determined branch address details are incomplete. + +=item * cancel_success + +The interface's cancel_request method was successful in cancelling the +Illrequest using the API. + +=item * cancel_fail + +The interface's cancel_request method failed to cancel the Illrequest using +the API. + +=item * unavailable + +The interface's request method returned saying that the desired item is not +available for request. + +=head2 Class Methods + +=cut + +=head3 type + +=cut + +sub _type { + return 'Illrequest'; +} + +# XXX: Method to expose DBIx accessor for illrequestattributes relationship +sub illrequestattributes { + my ( $self ) = @_; + return Koha::Illrequestattributes->_new_from_dbic( + scalar $self->_result->illrequestattributes + ); +} + +# XXX: Method to expose DBIx accessor for borrower relationship +sub patron { + my ( $self ) = @_; + return Koha::Patron->_new_from_dbic( + scalar $self->_result->borrowernumber + ); +} + +sub load_backend { + my ( $self, $backend_id ) = @_; + + my @raw = qw/Koha Illbackends/; # Base Path + + my $backend_name = $backend_id || $self->backend; + $location = join "/", @raw, $backend_name, "Base.pm"; # File to load + $backend_class = join "::", @raw, $backend_name, "Base"; # Package name + require $location; + $self->{_my_backend} = $backend_class->new({ config => $self->_config }); + return $self; +} + +=head3 _backend + + my $backend = $abstract->_backend($new_backend); + my $backend = $abstract->_backend; + +Getter/Setter for our API object. + +=cut + +sub _backend { + my ( $self, $backend ) = @_; + $self->{_my_backend} = $backend if ( $backend ); + # Dynamically load our backend object, as late as possible. + $self->load_backend unless ( $self->{_my_backend} ); + return $self->{_my_backend}; +} + +=head3 _config + + my $config = $abstract->_config($config); + my $config = $abstract->_config; + +Getter/Setter for our config object. + +=cut + +sub _config { + my ( $self, $config ) = @_; + $self->{_my_config} = $config if ( $config ); + # Load our config object, as late as possible. + unless ( $self->{_my_config} ) { + $self->{_my_config} = Koha::Illrequest::Config->new; + } + return $self->{_my_config}; +} + +=head3 metadata + +=cut + +sub metadata { + my ( $self ) = @_; + return $self->_backend->metadata($self); +} + +=head3 _core_status_graph + +=cut + +sub _core_status_graph { + my ( $self ) = @_; + return { + NEW => { + prev_actions => [ ], # Actions containing buttons + # leading to this status + id => 'NEW', # ID of this status + name => 'New request', # UI name of this status + ui_method_name => 'New request', # UI name of method leading + # to this status + method => 'create', # method to this status + next_actions => [ 'REQ', 'GENREQ', 'KILL' ], # buttons to add to all + # requests with this status + ui_method_icon => 'fa-plus', # UI Style class + }, + REQ => { + prev_actions => [ 'NEW', 'REQREV', 'QUEUED' ], + id => 'REQ', + name => 'Requested', + ui_method_name => 'Confirm request', + method => 'confirm', + next_actions => [ 'REQREV', 'CANCREQ' ], + ui_method_icon => 'fa-check', + }, + GENREQ => { + prev_actions => [ 'NEW', 'REQREV' ], + id => 'GENREQ', + name => 'Requested from partners', + ui_method_name => 'Place request with partners', + method => 'generic_confirm', + next_actions => [ 'COMP' ], + ui_method_icon => 'fa-send-o', + }, + REQREV => { + prev_actions => [ 'CANCREQ', 'REQ' ], + id => 'REQREV', + name => 'Request reverted', + ui_method_name => 'Revert Request', + method => 'cancel', + next_actions => [ 'REQ', 'GENREQ', 'KILL' ], + ui_method_icon => 'fa-times', + }, + QUEUED => { + prev_actions => [ ], + id => 'QUEUED', + name => 'Queued request', + ui_method_name => 0, + method => 0, + next_actions => [ 'REQ', 'KILL' ], + ui_method_icon => 0, + }, + CANCREQ => { + prev_actions => [ 'REQ' ], + id => 'CANCREQ', + name => 'Cancellation requested', + ui_method_name => 0, + method => 0, + next_actions => [ 'REQREV' ], + ui_method_icon => 0, + }, + COMP => { + prev_actions => [ 'REQ' ], + id => 'COMP', + name => 'Completed', + ui_method_name => 0, + method => 0, + next_actions => [ ], + ui_method_icon => 0, + }, + KILL => { + prev_actions => [ 'QUEUED', 'REQREV', 'NEW' ], + id => 'KILL', + name => 0, + ui_method_name => 'Delete request', + method => 'delete', + next_actions => [ ], + ui_method_icon => 'fa-trash', + }, + }; +} + +sub _status_graph_union { + my ( $self, $core_status_graph, $backend_status_graph ) = @_; + # Create new status graph with: + # - all core_status_graph + # - for-each each backend_status_graph + # + add to new status graph + # + for each core prev_action: + # * locate core_status + # * update next_actions with additional next action. + # + for each core next_action: + # * locate core_status + # * update prev_actions with additional prev action + + my @core_status_ids = keys %{$core_status_graph}; + my $status_graph = clone($core_status_graph); + + foreach my $backend_status_key ( keys %{$backend_status_graph} ) { + $backend_status = $backend_status_graph->{$backend_status_key}; + # Add to new status graph + $status_graph->{$backend_status_key} = $backend_status; + # Update all core methods' next_actions. + foreach my $prev_action ( @{$backend_status->{prev_actions}} ) { + if ( grep $prev_action, @core_status_ids ) { + my @next_actions = + @{$status_graph->{$prev_action}->{next_actions}}; + push @next_actions, $backend_status_key; + $status_graph->{$prev_action}->{next_actions} + = \@next_actions; + } + } + # Update all core methods' prev_actions + foreach my $next_action ( @{$backend_status->{next_actions}} ) { + if ( grep $next_action, @core_status_ids ) { + my @prev_actions = + @{$status_graph->{$next_action}->{prev_actions}}; + push @prev_actions, $backend_status_key; + $status_graph->{$next_action}->{prev_actions} + = \@prev_actions; + } + } + } + + return $status_graph; +} + +### Core API methods + +=head3 capabilities + + my $capabilities = $illrequest->capabilities; + +Return a hashref mapping methods to operation names supported by the queried +backend. + +Example return value: + + { create => "Create Request", confirm => "Progress Request" } + +=cut + +sub capabilities { + my ( $self, $status ) = @_; + # Generate up to date status_graph + my $status_graph = $self->_status_graph_union( + $self->_core_status_graph, + $self->_backend->status_graph({ + request => $self, + other => {} + }) + ); + # Extract available actions from graph. + return $status_graph->{$status} if $status; + # Or return entire graph. + return $status_graph; +} + +=head3 custom_capability + +Return the result of invoking $CANDIDATE on this request's backend with +$PARAMS, or 0 if $CANDIDATE is an unknown method on backend. + +=cut + +sub custom_capability { + my ( $self, $candidate, $params ) = @_; + foreach my $capability ( values $self->capabilities ) { + if ( $candidate eq $capability->{method} ) { + my $response = + $self->_backend->$candidate({ + request => $self, + other => $params, + }); + return $self->expandTemplate($response); + } + } + return 0; +} + +sub available_backends { + my ( $self ) = @_; + my $backend_dir = $self->_config->backend_dir; + my @backends = (); + my @backends = <$backend_dir/*> if ( $backend_dir ); + my @backends = map { basename($_) } @backends; + return \@backends; +} + +sub available_actions { + my ( $self ) = @_; + my $current_action = $self->capabilities($self->status); + my @available_actions = map { $self->capabilities($_) } + @{$current_action->{next_actions}}; + return \@available_actions; +} + +sub backend_confirm { + my ( $self, $params ) = @_; + + # The backend handles setting of mandatory fields in the commit stage: + # - orderid + # - accessurl, cost (if available). + my $response = $self->_backend->confirm({ + request => $self, + other => $params, + }); + return $self->expandTemplate($response); +} + +sub backend_update_status { + my ( $self, $params ) = @_; + return $self->expandTemplate($self->_backend->update_status($params)); +} + +=head3 backend_cancel + + my $ILLResponse = $illRequest->backend_cancel; + +The standard interface method allowing for request cancellation. + +=cut + +sub backend_cancel { + my ( $self, $params ) = @_; + + my $result = $self->_backend->cancel({ + request => $self, + other => $params + }); + + return $self->expandTemplate($result); +} + +=head3 backend_renew + + my $renew_response = $illRequest->backend_renew; + +The standard interface method allowing for request renewal queries. + +=cut + +sub backend_renew { + my ( $self ) = @_; + return $self->expandTemplate( + $self->_backend->renew({ + request => $self, + }) + ); +} + +=head3 backend_create + + my $create_response = $abstractILL->backend_create($params); + +Return an array of Record objects created by querying our backend with +a Search query. + +In the context of the other ILL methods, this is a special method: we only +pass it $params, as it does not yet have any other data associated with it. + +=cut + +sub backend_create { + my ( $self, $params ) = @_; + + # First perform API action, then... + my $args = { + request => $self, + other => $params, + }; + my $result = $self->_backend->create($args); + + # ... simple case: we're not at 'commit' stage. + my $stage = $result->{stage}; + return $self->expandTemplate($result) + unless ( 'commit' eq $stage ); + + # ... complex case: commit! + + # Do we still have space for an ILL or should we queue? + my $permitted = $self->check_limits( + { patron => $self->patron }, { librarycode => $self->branchcode } + ); + + # Now augment our committed request. + + $result->{permitted} = $permitted; # Queue request? + + # This involves... + + # ...Updating status! + $self->status('QUEUED')->store unless ( $permitted ); + + # FIXME: Fix Unmediated ILLs! + # Handle Unmediated ILLs + # if ( C4::Context->preference("UnmediatedILL") && $permitted + # # XXX: Why && result manual? + # && $result->{manual} ) { + # # FIXME: Also carry out privilege checks + # my ( $response, $new_rq ) = $self->place_request; # WTF? + # if ( $response ) { + # $result->{value}->{request} = $new_rq; + # return $result; + # } else { + # die "Placing the request failed."; + # } + # } else { + # $result->{value}->{request} = $request; + # return $result; + # } + + return $self->expandTemplate($result); +} + +=head3 expandTemplate + + my $params = $abstract->expandTemplate($params); + +Return a version of $PARAMS augmented with our required template path. + +=cut + +sub expandTemplate { + my ( $self, $params ) = @_; + my $backend = $self->_backend->name; + # Generate path to file to load + my $backend_dir = $self->_config->backend_dir; + my $backend_tmpl = join "/", $backend_dir, $backend; + my $intra_tmpl = join "/", $backend_tmpl, "intra-includes", + $params->{method} . ".inc"; + my $opac_tmpl = join "/", $backend_tmpl, "opac-includes", + $params->{method} . ".inc"; + # Set files to load + $params->{template} = $intra_tmpl; + $params->{opac_template} = $opac_tmpl; + return $params; +} + +#### Abstract Imports + +=head3 getCensorNotesStaff + + my $bool = $abstract->getCensorNotesStaff; + +Return a boolean indicating whether we should be censoring staff notes or not, +as determined by our configuration file. + +=cut + +sub getCensorNotesStaff { + my ( $self ) = @_; + my $censorship = $self->_config->censorship; + return $censorship->{censor_notes_staff}; +} + +=head3 getDisplayReplyDate + + my 0 = $abstract->getDisplayReplyDate; + +Return a 0 if we want to hide it or 1 if not. + +=cut + +sub getDisplayReplyDate { + my ( $self ) = @_; + my $censorship = $self->_config->censorship; + # If censor is yes, don't display and vice versa. + return ( $censorship->{censor_reply_date} ) ? 0 : 1; +} + +=head3 getLimits + + my $limit_rules = $abstract->getLimits( { + type => 'brw_cat' | 'branch', + value => $value + } ); + +Return the ILL limit rules for the supplied combination of type / value. + +As the config may have no rules for this particular type / value combination, +or for the default, we must define fall-back values here. + +=cut + +# FIXME: Needs unit tests! +sub getLimits { + my ( $self, $params ) = @_; + my $limits = $self->_config->getLimitRules($params->{type}); + + return $limits->{$params->{value}} + || $limits->{default} + || { count => -1, method => 'active' }; +} + +=head3 getPrefix + + my $prefix = $abstract->getPrefix( { + brw_cat => $brw_cat, + branch => $branch_code, + } ); + +Return the ILL prefix as defined by our $params: either per borrower category, +per branch or the default. + +=cut + +sub getPrefix { + my ( $self, $params ) = @_; + my $brn_prefixes = $self->_config->getPrefixes('branch'); + my $brw_prefixes = $self->_config->getPrefixes('brw_cat'); + + return $brw_prefixes->{$params->{brw_cat}} + || $brn_prefixes->{$params->{branch}} + || $brw_prefixes->{default} + || ""; # "the empty prefix" +} + +#### Illrequests Imports + +=head3 check_limits + + my $ok = $illRequests->check_limits( { + borrower => $borrower, + branchcode => 'branchcode' | undef, + } ); + +Given $PARAMS, a hashref containing a $borrower object and a $branchcode, +see whether we are still able to place ILLs. + +LimitRules are derived from koha-conf.xml: + + default limit counts, and counting method + + branch specific limit counts & counting method + + borrower category specific limit counts & counting method + + err on the side of caution: a counting fail will cause fail, even if + the other counts passes. + +=cut + +# FIXME: Needs unit tests! +sub check_limits { + my ( $self, $params ) = @_; + my $patron = $params->{patron}; + my $branchcode = $params->{librarycode} || $patron->branchcode; + + # Establish rules + my ( $branch_rules, $brw_rules ) = ( + $self->getLimits( { + type => 'branch', + value => $branchcode + } ), + $self->getLimits( { + type => 'brw_cat', + value => $patron->categorycode, + } ), + ); + # Almost there, but category code didn't quite work. + my ( $branch_limit, $brw_limit ) + = ( $branch_rules->{count}, $brw_rules->{count} ); + my ( $branch_count, $brw_count ) = ( + $self->_limit_counter( + $branch_rules->{method}, { branch_id => $branchcode } + ), + $self->_limit_counter( + $brw_rules->{method}, { borrower_id => $patron->borrowernumber } + ), + ); + + # Compare and return + # A limit of -1 means no limit exists. + # We return blocked if either branch limit or brw limit is reached. + if ( ( $branch_limit != -1 && $branch_limit <= $branch_count ) + || ( $brw_limit != -1 && $brw_limit <= $brw_count ) ) { + return 0; + } else { + return 1; + } +} + +# FIXME: Needs unit tests! +sub _limit_counter { + my ( $self, $method, $target ) = @_; + + # Establish parameters of counts + my $where; + if ($method && $method eq 'annual') { + $where = \"YEAR(placement_date) = YEAR(NOW())"; + } else { # assume 'active' + # FIXME: This status list is ugly. There should be a method in config + # to return these. + $where = { status => { -not_in => [ 'QUEUED', 'COMP' ] } }; + } + + # Create resultset + my $resultset = Koha::Illrequests->search({ %{$target}, %{$where} }); + + # Fetch counts + return $resultset->count; +} + +=head3 requires_moderation + + my $status = $illRequest->requires_moderation; + +Return the name of the status if moderation by staff is required; or 0 +otherwise. + +=cut + +sub requires_moderation { + my ( $self ) = @_; + my $require_moderation = { + 'CANCREQ' => 'CANCREQ', + }; + return $require_moderation->{$self->status}; +} + +=head3 generic_confirm + + my $stage_summary = $illRequest->generic_confirm; + +Handle the generic_confirm extended method. The first stage involves creating +a template email for the end user to edit in the browser. The second stage +attempts to submit the email. + +=cut + +sub generic_confirm { + my ( $self, $params ) = @_; + if ( !$params->{stage}|| $params->{stage} eq 'init' ) { + my $draft->{subject} = "ILL Request"; + $draft->{body} = <metadata; + while (my ($title, $value) = each %{$details}) { + $draft->{body} .= " - " . $title . ": " . $value . "\n" + if $value; + } + $draft->{body} .= <search({ + categorycode => $self->_config->partner_code + }); + return { + error => 0, + status => '', + message => '', + method => 'generic_confirm', + stage => 'draft', + value => { + draft => $draft, + partners => $partners, + } + }; + + } elsif ( 'draft' eq $params->{stage} ) { + # Create the to header + my $to = $params->{partners}; + $to =~ s/^\x00//; # Strip leading NULLs + $to =~ s/\x00/; /; # Replace others with '; ' + die "No target email addresses found. Either select at least one partner or check your ILL partner library records." if ( !$to ); + # Create the from, replyto and sender headers + $branch = Koha::Libraries->find($params->{current_branchcode}) + || die "Invalid current branchcode. Are you logged in as the database user?"; + my $from = $branch->branchemail; + my $replyto = $branch->branchreplyto || $from; + die "Your branch has no email address. Please set it." + if ( !$from ); + # Create the email + my $message = Koha::Email->new; + my %mail = $message->create_message_headers( + { + to => $to, + from => $from, + replyto => $replyto, + subject => Encode::encode( "utf8", $params->{subject} ), + message => Encode::encode( "utf8", $params->{body} ), + contenttype => 'text/plain', + } + ); + # Send it + my $result = sendmail(%mail); + if ( $result ) { + $self->status("GENREQ")->store; + return { + error => 0, + status => '', + message => '', + method => 'generic_confirm', + stage => 'commit', + next => 'illview', + }; + } else { + return { + error => 1, + status => 'email_failed', + message => $Mail::Sendmail::error, + method => 'generic_confirm', + stage => 'draft', + }; + } + } else { + die "Unknown stage, should not have happened." + } +} + +=head3 id_prefix + + my $prefix = $record->id_prefix; + +Return the prefix appropriate for the current Illrequest as derived from the +borrower and branch associated with this request's Status, and the config +file. + +=cut + +sub id_prefix { + my ( $self ) = @_; + # FIXME: can we automatically use borrowernumber as borrower? + my $brw = $self->patron; + my $brw_cat = "dummy"; + $brw_cat = $brw->categorycode + unless ( 'HASH' eq ref($brw) && $brw->{deleted} ); + my $prefix = $self->getPrefix( { + brw_cat => $brw_cat, + branch => $self->branchcode, + } ); + $prefix .= "-" if ( $prefix ); + return $prefix; +} + +=head3 _censor + + my $params = $illRequest->_censor($params); + +Return $params, modified to reflect our censorship requirements. + +=cut + +sub _censor { + my ( $self, $params ) = @_; + $params->{censor_notes_staff} = $self->getCensorNotesStaff + if ( $params->{opac} ); + $params->{display_reply_date} = $self->getDisplayReplyDate; + + return $params; +} + +=head1 AUTHOR + +Alex Sassmannshausen + +=cut + +1; diff --git a/Koha/Illrequest/Backend/Dummy/Base.pm b/Koha/Illrequest/Backend/Dummy/Base.pm new file mode 100644 index 0000000..b832bf8 --- /dev/null +++ b/Koha/Illrequest/Backend/Dummy/Base.pm @@ -0,0 +1,576 @@ +package Koha::Illrequest::Backend::Dummy::Base; + +# Copyright PTFS Europe 2014 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; +use DateTime; +use Koha::Illrequestattribute; + +=head1 NAME + +Koha::Illrequest::Backend::Dummy - Koha ILL Backend: Dummy + +=head1 SYNOPSIS + +Koha ILL implementation for the "Dummy" backend. + +=head1 DESCRIPTION + +=head2 Overview + +We will be providing the Abstract interface which requires we implement the +following methods: +- create -> initial placement of the request for an ILL order +- confirm -> confirm placement of the ILL order +- list -> list all ILL Requests currently placed with the backend +- renew -> request a currently borrowed ILL be renewed in the backend +- update_status -> ILL module update hook: custom actions on status update +- cancel -> request an already 'confirm'ed ILL order be cancelled +- status -> request the current status of a confirmed ILL order + +Each of the above methods will receive the following parameter from +Illrequest.pm: + + { + request => $request, + other => $other, + } + +where: + +- $REQUEST is the Illrequest object in Koha. It's associated + Illrequestattributes can be accessed through the `illrequestattributes` + method. +- $OTHER is any further data, generally provided through templates .INCs + +Each of the above methods should return a hashref of the following format: + + return { + error => 0, + # ^------- 0|1 to indicate an error + status => 'result_code', + # ^------- Summary of the result of the operation + message => 'Human readable message.', + # ^------- Message, possibly to be displayed + # Normally messages are derived from status in INCLUDE. + # But can be used to pass API messages to the INCLUDE. + method => 'list', + # ^------- Name of the current method invoked. + # Used to load the appropriate INCLUDE. + stage => 'commit', + # ^------- The current stage of this method + # Used by INCLUDE to determine HTML to generate. + # 'commit' will result in final processing by Illrequest.pm. + next => 'illview'|'illlist', + # ^------- When stage is 'commit', should we move on to ILLVIEW the + # current request or ILLLIST all requests. + value => {}, + # ^------- A hashref containing an arbitrary return value that this + # backend wants to supply to its INCLUDE. + }; + +=head2 On the Dummy backend + +The Dummy backend is rather simple, but provides correctly formatted response +values, that other backends can model themselves after. + +The code is not DRY -- primarily so that each method can be looked at in +isolation rather than having to familiarise oneself with helper procedures. + +=head1 API + +=head2 Class Methods + +=cut + +=head3 new + + my $backend = Koha::Illrequest::Backend::Dummy->new; + +=cut + +sub new { + # -> instantiate the backend + my ( $class ) = @_; + my $self = {}; + bless( $self, $class ); + return $self; +} + +=head3 _data_store + + my $request = $self->_data_store($id); + my $requests = $self->_data_store; + +A mock of a data store. When passed no parameters it returns all entries. +When passed one it will return the entry matched by its id. + +=cut + +sub _data_store { + my $data = { + 1234 => { + id => 1234, + title => "Ordering ILLs using Koha", + author => "A.N. Other", + }, + 5678 => { + id => 5678, + title => "Interlibrary loans in Koha", + author => "A.N. Other", + }, + }; + # ID search + my ( $self, $id ) = @_; + return $data->{$id} if $id; + + # Full search + my @entries; + while ( my ( $k, $v ) = each %{$data} ) { + push @entries, $v; + } + return \@entries; +} + +=head3 create + + my $response = $backend->create({ + request => $requestdetails, + other => $other, + }); + +This is the initial creation of the request. Generally this stage will be +some form of search with the backend. + +By and large we will not have useful $requestdetails (borrowernumber, +branchcode, status, etc.). + +$params is simply an additional slot for any further arbitrary values to pass +to the backend. + +This is an example of a multi-stage method. + +=cut + +sub create { + # -> initial placement of the request for an ILL order + my ( $self, $params ) = @_; + my $stage = $params->{other}->{stage}; + if ( !$stage || $stage eq 'init' ) { + # We simply need our template .INC to produce a search form. + return { + error => 0, + status => '', + message => '', + method => 'create', + stage => 'search_form', + value => {}, + }; + } elsif ( $stage eq 'search_form' ) { + # Received search query in 'other'; perform search... + + # No-op on Dummy + + # and return results. + return { + error => 0, + status => '', + message => '', + method => 'create', + stage => 'search_results', + value => { + borrowernumber => $params->{other}->{borrowernumber}, + branchcode => $params->{other}->{branchcode}, + medium => $params->{other}->{medium}, + candidates => $self->_data_store, + } + }; + } elsif ( $stage eq 'search_results' ) { + # We have a selection + my $id = $params->{other}->{id}; + + # -> select from backend... + my $request_details = $self->_data_store($id); + + # ...Populate Illrequest + my $request = $params->{request}; + $request->borrower_id($params->{other}->{borrowernumber}); + $request->branch_id($params->{other}->{branchcode}); + $request->medium($params->{other}->{medium}); + $request->status('NEW'); + $request->placed(DateTime->now); + $request->updated(DateTime->now); + $request->store; + # ...Populate Illrequestattributes + while ( my ( $type, $value ) = each %{$request_details} ) { + Koha::Illrequestattribute->new({ + illrequest_id => $request->illrequest_id, + type => $type, + value => $value, + })->store; + } + + # -> create response. + return { + error => 0, + status => '', + message => '', + method => 'create', + stage => 'commit', + next => 'illview', + value => $request_details, + }; + } else { + # Invalid stage, return error. + return { + error => 1, + status => 'unknown_stage', + message => '', + method => 'create', + stage => $params->{stage}, + value => {}, + }; + } +} + +=head3 confirm + + my $response = $backend->confirm({ + request => $requestdetails, + other => $other, + }); + +Confirm the placement of the previously "selected" request (by using the +'create' method). + +In this case we will generally use $request. +This will be supplied at all times through Illrequest. $other may be supplied +using templates. + +=cut + +sub confirm { + # -> confirm placement of the ILL order + my ( $self, $params ) = @_; + # Turn Illrequestattributes into a plain hashref + my $value = {}; + my $attributes = $params->{request}->illrequestattributes; + foreach my $attr (@{$attributes->as_list}) { + $value->{$attr->type} = $attr->value; + }; + # Submit request to backend... + + # No-op for Dummy + + # ...parse response... + $attributes->find_or_create({ type => "status", value => "On order" }); + my $request = $params->{request}; + $request->cost("30 GBP"); + $request->orderid($value->{id}); + $request->status("REQ"); + $request->accessurl("URL") if $value->{url}; + $request->store; + $value->{status} = "On order"; + $value->{cost} = "30 GBP"; + # ...then return our result: + return { + error => 0, + status => '', + message => '', + method => 'confirm', + stage => 'commit', + next => 'illview', + value => $value, + }; +} + +=head3 list + + my $response = $backend->list({ + request => $requestdetails, + other => $other, + }; + +Attempt to get a list of the currently registered requests with the backend. + +Parameters are optional for this request. A backend may be supplied with +details of a specific request (or a group of requests in $other), but equally +no parameters might be provided at all. + +Normally no parameters will be provided in the 'create' stage. After this, +parameters may be provided using templates. + +=cut + +sub list { + # -> list all ILL Requests currently placed with the backend + # (we ignore all params provided) + my ( $self, $params ) = @_; + my $stage = $params->{other}->{stage}; + if ( !$stage || $stage eq 'init' ) { + return { + error => 0, + status => '', + message => '', + method => 'list', + stage => 'list', + value => { + 1 => { + id => 1234, + title => "Ordering ILLs using Koha", + author => "A.N. Other", + status => "On order", + cost => "30 GBP", + }, + }, + }; + } elsif ( $stage eq 'list' ) { + return { + error => 0, + status => '', + message => '', + method => 'list', + stage => 'commit', + value => {}, + }; + } else { + # Invalid stage, return error. + return { + error => 1, + status => 'unknown_stage', + message => '', + method => 'create', + stage => $params->{stage}, + value => {}, + }; + } +} + +=head3 renew + + my $response = $backend->renew({ + request => $requestdetails, + other => $other, + }); + +Attempt to renew a request that was supplied through backend and is currently +in use by us. + +We will generally use $request. This will be supplied at all times through +Illrequest. $other may be supplied using templates. + +=cut + +sub renew { + # -> request a currently borrowed ILL be renewed in the backend + my ( $self, $params ) = @_; + # Turn Illrequestattributes into a plain hashref + my $value = {}; + my $attributes = $params->{request}->illrequestattributes; + foreach my $attr (@{$attributes->as_list}) { + $value->{$attr->type} = $attr->value; + }; + # Submit request to backend, parse response... + my ( $error, $status, $message ) = ( 0, '', '' ); + if ( !$value->{status} || $value->{status} eq 'On order' ) { + $error = 1; + $status = 'not_renewed'; + $message = 'Order not yet delivered.'; + } else { + $value->{status} = "Renewed"; + } + # ...then return our result: + return { + error => $error, + status => $status, + message => $message, + method => 'renew', + stage => 'commit', + value => $value, + }; +} + +=head3 update_status + + my $response = $backend->update_status({ + request => $requestdetails, + other => $other, + }); + +Our Illmodule is handling a request to update the status of an Illrequest. As +part of this we give the backend an opportunity to perform arbitrary actions +on update to a new status. + +We will provide $request. This will be supplied at all times through +Illrequest. $other will contain entries for the old status and the new +status, as well as other information provided from templates. + +$old_status, $new_status. + +=cut + +sub update_status { + # -> ILL module update hook: custom actions on status update + my ( $self, $params ) = @_; + # Turn Illrequestattributes into a plain hashref + my $value = {}; + my $attributes = $params->{request}->illrequestattributes; + foreach my $attr (@{$attributes->as_list}) { + $value->{$attr->type} = $attr->value; + }; + # Submit request to backend, parse response... + my ( $error, $status, $message ) = (0, '', ''); + my $old = $params->{other}->{old_status}; + my $new = $params->{other}->{new_status}; + if ( !$new || $new eq 'ERR' ) { + ( $error, $status, $message ) = ( + 1, 'failed_update_hook', + 'Fake reason for failing to perform update operation.' + ); + } + return { + error => $error, + status => $status, + message => $message, + method => 'update_status', + stage => 'commit', + value => $value, + }; +} + +=head3 cancel + + my $response = $backend->cancel({ + request => $requestdetails, + other => $other, + }); + +We will attempt to cancel a request that was confirmed. + +We will generally use $request. This will be supplied at all times through +Illrequest. $other may be supplied using templates. + +=cut + +sub cancel { + # -> request an already 'confirm'ed ILL order be cancelled + my ( $self, $params ) = @_; + # Turn Illrequestattributes into a plain hashref + my $value = {}; + my $attributes = $params->{request}->illrequestattributes; + foreach my $attr (@{$attributes->as_list}) { + $value->{$attr->type} = $attr->value; + }; + # Submit request to backend, parse response... + my ( $error, $status, $message ) = (0, '', ''); + if ( !$value->{status} ) { + ( $error, $status, $message ) = ( + 1, 'unknown_request', 'Cannot cancel an unknown request.' + ); + } else { + $attributes->find({ type => "status" })->delete; + $params->{request}->status("REQREV"); + $params->{request}->cost(undef); + $params->{request}->orderid(undef); + $params->{request}->store; + } + return { + error => $error, + status => $status, + message => $message, + method => 'cancel', + stage => 'commit', + value => $value, + }; +} + +=head3 status + + my $response = $backend->create({ + request => $requestdetails, + other => $other, + }); + +We will try to retrieve the status of a specific request. + +We will generally use $request. This will be supplied at all times through +Illrequest. $other may be supplied using templates. + +=cut + +sub status { + # -> request the current status of a confirmed ILL order + my ( $self, $params ) = @_; + my $value = {}; + my $stage = $params->{other}->{stage}; + my ( $error, $status, $message ) = (0, '', ''); + if ( !$stage || $stage eq 'init' ) { + # Generate status result + # Turn Illrequestattributes into a plain hashref + my $attributes = $params->{request}->illrequestattributes; + foreach my $attr (@{$attributes->as_list}) { + $value->{$attr->type} = $attr->value; + } + ; + # Submit request to backend, parse response... + if ( !$value->{status} ) { + ( $error, $status, $message ) = ( + 1, 'unknown_request', 'Cannot query status of an unknown request.' + ); + } + return { + error => $error, + status => $status, + message => $message, + method => 'status', + stage => 'status', + value => $value, + }; + + } elsif ( $stage eq 'status') { + # No more to do for method. Return to illlist. + return { + error => $error, + status => $status, + message => $message, + method => 'status', + stage => 'commit', + next => 'illlist', + value => {}, + }; + + } else { + # Invalid stage, return error. + return { + error => 1, + status => 'unknown_stage', + message => '', + method => 'create', + stage => $params->{stage}, + value => {}, + }; + } +} + +=head1 AUTHOR + +Alex Sassmannshausen + +=cut + +1; diff --git a/Koha/Illrequest/Config.pm b/Koha/Illrequest/Config.pm new file mode 100644 index 0000000..159e5e5 --- /dev/null +++ b/Koha/Illrequest/Config.pm @@ -0,0 +1,389 @@ +package Koha::Illrequest::Config; + +# Copyright 2013,2014 PTFS Europe Ltd +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; +use C4::Context; + +=head1 NAME + +Koha::Illrequest::Config - Koha ILL Configuration Object + +=head1 SYNOPSIS + +Object-oriented class that giving access to the illconfig data derived +from ill/config.yaml. + +=head1 DESCRIPTION + +Config object providing abstract representation of the expected XML +returned by ILL API. + +In particular the config object uses a YAML file, whose path is +defined by in koha-conf.xml. That YAML file provides the +data structure exposed in this object. + +By default the configured data structure complies with fields used by +the British Library Interlibrary Loan DSS API. + +The config file also provides mappings for Record Object accessors. + +FIXME: illcomm: In general Config should be split into two parts: +those that are high-level and those that are backend specific. The +latter should not get specific accessors, but rather a generic +accessor, that takes a setting name as string parameter. + +=head1 API + +=head2 Class Methods + +=head3 new + + my $config = Koha::Illrequest::Config->new(); + +Create a new Koha::Illrequest::Config object, with mapping data loaded from the +ILL configuration file. + +=cut + +sub new { + my ( $class, $test ) = @_; + my $self = {}; + + $self->{configuration} = _load_configuration( + C4::Context->config("interlibrary_loans"), + C4::Context->preference("UnmediatedILL") + ) unless ( $test ); + + bless $self, $class; + + return $self; +} + +=head3 backend + + $backend = $config->backend($name); + $backend = $config->backend; + +Standard setter/accessor for our backend. + +=cut + +sub backend { + my ( $self, $new ) = @_; + $self->{configuration}->{backend} = $new if $new; + return $self->{configuration}->{backend}; +} + +=head3 backend_dir + + $backend_dir = $config->backend_dir($new_path); + $backend_dir = $config->backend_dir; + +Standard setter/accessor for our backend_directory. + +=cut + +sub backend_dir { + my ( $self, $new ) = @_; + $self->{configuration}->{backend_directory} = $new if $new; + return $self->{configuration}->{backend_directory}; +} + +=head3 partner_code + + $partner_code = $config->partner_code($new_path); + $partner_code = $config->partner_code; + +Standard setter/accessor for our partner_code. + +=cut + +sub partner_code { + my ( $self, $new ) = @_; + $self->{configuration}->{partner_code} = $new if $new; + return $self->{configuration}->{partner_code}; +} + +=head3 limits + + $limits = $config->limits($limitshash); + $limits = $config->limits; + +Standard setter/accessor for our limits. No parsing is performed on +$LIMITSHASH, so caution should be exercised when using this setter. + +=cut + +sub limits { + my ( $self, $new ) = @_; + $self->{configuration}->{limits} = $new if $new; + return $self->{configuration}->{limits}; +} + +=head3 getPrefixes + + my $prefixes = $config->getPrefixes('brw_cat' | 'branch'); + +Return the prefix for ILLs defined by our config. + +=cut + +sub getPrefixes { + my ( $self, $type ) = @_; + die "Unexpected type." unless ( $type eq 'brw_cat' || $type eq 'branch' ); + my $values = $self->{configuration}->{prefixes}->{$type}; + $values->{default} = $self->{configuration}->{prefixes}->{default}; + return $values; +} + +=head3 getLimitRules + + my $rules = $config->getLimitRules('brw_cat' | 'branch') + +Return the hash of ILL limit rules defined by our config. + +=cut + +sub getLimitRules { + my ( $self, $type ) = @_; + die "Unexpected type." unless ( $type eq 'brw_cat' || $type eq 'branch' ); + my $values = $self->{configuration}->{limits}->{$type}; + $values->{default} = $self->{configuration}->{limits}->{default}; + return $values; +} + +=head3 getDigitalRecipients + + my $recipient_rules= $config->getDigitalRecipients('brw_cat' | 'branch'); + +Return the hash of digital_recipient settings defined by our config. + +=cut + +sub getDigitalRecipients { + my ( $self, $type ) = @_; + die "Unexpected type." unless ( $type eq 'brw_cat' || $type eq 'branch' ); + my $values = $self->{configuration}->{digital_recipients}->{$type}; + $values->{default} = + $self->{configuration}->{digital_recipients}->{default}; + return $values; +} + +=head3 censorship + + my $censoredValues = $config->censorship($hash); + my $censoredValues = $config->censorship; + +Standard setter/accessor for our limits. No parsing is performed on $HASH, so +caution should be exercised when using this setter. + +Return our censorship values for the OPAC as loaded from the koha-conf.xml, or +the fallback value (no censorship). + +=cut + +sub censorship { + my ( $self, $new ) = @_; + $self->{configuration}->{censorship} = $new if $new; + return $self->{configuration}->{censorship}; +} + +=head3 _load_configuration + + my $configuration = $config->_load_configuration($config_from_xml); + +Read the configuration values passed as the parameter, and populate a hashref +suitable for use with these. + +A key task performed here is the parsing of the input in the configuration +file to ensure we have only valid input there. + +=cut + +sub _load_configuration { + my ( $xml_config, $unmediated ) = @_; + my $xml_backend_dir = $xml_config->{backend_directory}; + + # Default data structure to be returned + my $configuration = { + backend_directory => $xml_backend_dir, + censorship => { + censor_notes_staff => 0, + censor_reply_date => 0, + }, + limits => {}, + digital_recipients => {}, + prefixes => {}, + partner_code => 'ILLLIBS', + }; + + # Per Branch Configuration + my $branches = $xml_config->{branch}; + if ( ref($branches) eq "ARRAY" ) { + # Multiple branch overrides defined + map { + _load_unit_config({ + unit => $_, + id => $_->{code}, + config => $configuration, + type => 'branch' + }) + } @{$branches}; + } elsif ( ref($branches) eq "HASH" ) { + # Single branch override defined + _load_unit_config({ + unit => $branches, + id => $branches->{code}, + config => $configuration, + type => 'branch' + }); + } + + # Per Borrower Category Configuration + my $brw_cats = $xml_config->{borrower_category}; + if ( ref($brw_cats) eq "ARRAY" ) { + # Multiple borrower category overrides defined + map { + _load_unit_config({ + unit => $_, + id => $_->{code}, + config => $configuration, + type => 'brw_cat' + }) + } @{$brw_cats}; + } elsif ( ref($brw_cats) eq "HASH" ) { + # Single branch override defined + _load_unit_config({ + unit => $brw_cats, + id => $brw_cats->{code}, + config => $configuration, + type => 'brw_cat' + }); + } + + # Default Configuration + _load_unit_config({ + unit => $xml_config, + id => 'default', + config => $configuration + }); + + # Censorship + my $staff_comments = $xml_config->{staff_request_comments}; + $configuration->{censorship}->{censor_notes_staff} = 1 + if ( $staff_comments && 'hide' eq $staff_comments ); + my $reply_date = $xml_config->{reply_date}; + if ( 'hide' eq $reply_date ) { + $configuration->{censorship}->{censor_reply_date} = 1; + } else { + $configuration->{censorship}->{censor_reply_date} = $reply_date; + } + + # ILL Partners + $configuration->{partner_code} = $xml_config->{partner_code} || 'ILLLIBS'; + + die "No DEFAULT_FORMATS has been defined in koha-conf.xml, but UNMEDIATEDILL is active." + if ( $unmediated && !$configuration->{default_formats}->{default} ); + + return $configuration; +} + +=head3 _load_unit_config + + my $configuration->{part} = _load_unit_config($params); + +$PARAMS is a hashref with the following elements: +- unit: the part of the configuration we are parsing. +- id: the name within which we will store the parsed unit in config. +- config: the configuration we are augmenting. +- type: the type config unit we are parsing. Assumed to be 'default'. + +Read `unit', and augment `config' with these under `id'. + +This is a helper for _load_configuration. + +A key task performed here is the parsing of the input in the configuration +file to ensure we have only valid input there. + +=cut + +sub _load_unit_config { + my ( $params ) = @_; + my $unit = $params->{unit}; + my $id = $params->{id}; + my $config = $params->{config}; + my $type = $params->{type}; + return $config unless $id; + + if ( $unit->{api_key} && $unit->{api_auth} ) { + $config->{credentials}->{api_keys}->{$id} = { + api_key => $unit->{api_key}, + api_auth => $unit->{api_auth}, + }; + } + # Add request_limit rules. + # METHOD := 'annual' || 'active' + # COUNT := x >= -1 + if ( ref $unit->{request_limit} eq 'HASH' ) { + my $method = $unit->{request_limit}->{method}; + my $count = $unit->{request_limit}->{count}; + if ( 'default' eq $id ) { + $config->{limits}->{$id}->{method} = $method + if ( $method && ( 'annual' eq $method || 'active' eq $method ) ); + $config->{limits}->{$id}->{count} = $count + if ( $count && ( -1 <= $count ) ); + } else { + $config->{limits}->{$type}->{$id}->{method} = $method + if ( $method && ( 'annual' eq $method || 'active' eq $method ) ); + $config->{limits}->{$type}->{$id}->{count} = $count + if ( $count && ( -1 <= $count ) ); + } + } + + # Add prefix rules. + # PREFIX := string + if ( $unit->{prefix} ) { + if ( 'default' eq $id ) { + $config->{prefixes}->{$id} = $unit->{prefix}; + } else { + $config->{prefixes}->{$type}->{$id} = $unit->{prefix}; + } + } + + # Add digital_recipient rules. + # DIGITAL_RECIPIENT := borrower || branch (defaults to borrower) + if ( $unit->{digital_recipient} ) { + if ( 'default' eq $id ) { + $config->{digital_recipients}->{$id} = $unit->{digital_recipient}; + } else { + $config->{digital_recipients}->{$type}->{$id} = + $unit->{digital_recipient}; + } + } + + return $config; +} + +=head1 AUTHOR + +Alex Sassmannshausen + +=cut + +1; diff --git a/Koha/Illrequestattribute.pm b/Koha/Illrequestattribute.pm new file mode 100644 index 0000000..16bc086 --- /dev/null +++ b/Koha/Illrequestattribute.pm @@ -0,0 +1,51 @@ +package Koha::Illrequestattribute; + +# Copyright PTFS Europe 2016 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# Koha; if not, write to the Free Software Foundation, Inc., 51 Franklin +# Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Koha::Database; + +use base qw(Koha::Object); + +=head1 NAME + +Koha::Illrequestattribute - Koha Illrequestattribute Object class + +=head1 API + +=head2 Class Methods + +=cut + +=head3 type + +=cut + +sub _type { + return 'Illrequestattribute'; +} + +=head1 AUTHOR + +Alex Sassmannshausen + +=cut + +1; diff --git a/Koha/Illrequestattributes.pm b/Koha/Illrequestattributes.pm new file mode 100644 index 0000000..e05fa6e --- /dev/null +++ b/Koha/Illrequestattributes.pm @@ -0,0 +1,55 @@ +package Koha::Illrequestattributes; + +# Copyright PTFS Europe 2016 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Koha::Database; +use Koha::Illrequestattribute; + +use base qw(Koha::Objects); + +=head1 NAME + +Koha::Illrequestattributes - Koha Illrequestattributes Object class + +=head1 API + +=head2 Class Methods + +=cut + +=head3 type + +=cut + +sub _type { + return 'Illrequestattribute'; +} + +sub object_class { + return 'Koha::Illrequestattribute'; +} + +=head1 AUTHOR + +Alex Sassmannshausen + +=cut + +1; diff --git a/Koha/Illrequests.pm b/Koha/Illrequests.pm new file mode 100644 index 0000000..4bfd865 --- /dev/null +++ b/Koha/Illrequests.pm @@ -0,0 +1,99 @@ +package Koha::Illrequests; + +# Copyright PTFS Europe 2016 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Koha::Database; +use Koha::Illrequest; +use Koha::Illrequest::Config; + +use base qw(Koha::Objects); + +=head1 NAME + +Koha::Illrequests - Koha Illrequests Object class + +=head1 API + +=head2 Class Methods + +=cut + +=head3 type + +=cut + +sub _type { + return 'Illrequest'; +} + +sub object_class { + return 'Koha::Illrequest'; +} + +##### To be implemented Facade + +=head3 new + + my $illRequests = Koha::Illrequests->new(); + +Create an ILLREQUESTS object, a singleton through which we can interact with +ILLREQUEST objects stored in the database or search for ILL candidates at API +backends. + +=cut + +sub new { + my ( $class, $attributes ) = @_; + + my $self = $class->SUPER::new($class, $attributes); + + # FIXME: Unless we can inject config into Illrequest objects, strip this + # out. + my $config = Koha::Illrequest::Config->new; # <- Necessary + $self->{_config} = $config; # <- Necessary + + return $self; +} + +=head3 search_incomplete + + my $requests = $illRequests->search_incomplete; + +A specialised version of `search`, returning all requests currently +not considered completed. + +=cut + +sub search_incomplete { + my ( $self ) = @_; + $self->search( { + status => [ + -and => { '!=', 'COMP' }, { '!=', 'GENCOMP' } + ] + } ); +} + +=head1 AUTHOR + +Alex Sassmannshausen + +=cut + +1; diff --git a/Koha/REST/V1/Illrequests.pm b/Koha/REST/V1/Illrequests.pm new file mode 100644 index 0000000..0807eb1 --- /dev/null +++ b/Koha/REST/V1/Illrequests.pm @@ -0,0 +1,85 @@ +package Koha::REST::V1::Illrequests; + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Mojo::Base 'Mojolicious::Controller'; + +use Koha::Illrequests; +use Koha::Library; + +sub list { + my ($c, $args, $cb) = @_; + + my $filter; + $args //= {}; + my $output = []; + + # Create a hash where all keys are embedded values + # Enables easy checking + my %embed; + if (defined $args->{embed}) { + %embed = map { $_ => 1 } @{$args->{embed}}; + delete $args->{embed}; + } + + for my $filter_param ( keys %$args ) { + my @values = split(/,/, $args->{$filter_param}); + $filter->{$filter_param} = \@values; + } + + my $requests = Koha::Illrequests->search($filter); + + while (my $request = $requests->next) { + my $unblessed = $request->unblessed; + # Add the request's id_prefix + $unblessed->{id_prefix} = $request->id_prefix; + # Augment the request response with patron details + # if appropriate + if (defined $embed{patron}) { + my $patron = $request->patron; + $unblessed->{patron} = { + firstname => $patron->firstname, + surname => $patron->surname, + cardnumber => $patron->cardnumber + }; + } + # Augment the request response with metadata details + # if appropriate + if (defined $embed{metadata}) { + $unblessed->{metadata} = $request->metadata; + } + # Augment the request response with status details + # if appropriate + if (defined $embed{capabilities}) { + $unblessed->{capabilities} = $request->capabilities; + } + # Augment the request response with branch details + # if appropriate + if (defined $embed{branch}) { + $unblessed->{branch} = Koha::Libraries->find( + $request->branchcode + )->unblessed; + } + push @{$output}, $unblessed + } + + return $c->$cb( $output, 200 ); + +} + +1; diff --git a/api/v1/swagger/paths.json b/api/v1/swagger/paths.json index b1fcf40..608d717 100644 --- a/api/v1/swagger/paths.json +++ b/api/v1/swagger/paths.json @@ -16,5 +16,8 @@ }, "/patrons/{borrowernumber}": { "$ref": "paths/patrons.json#/~1patrons~1{borrowernumber}" + }, + "/illrequests": { + "$ref": "paths/illrequests.json#/~1illrequests" } } diff --git a/api/v1/swagger/paths/illrequests.json b/api/v1/swagger/paths/illrequests.json new file mode 100644 index 0000000..ddafd80 --- /dev/null +++ b/api/v1/swagger/paths/illrequests.json @@ -0,0 +1,98 @@ +{ + "/illrequests": { + "get": { + "x-mojo-controller": "Koha::REST::V1::Illrequests", + "operationId": "list", + "tags": ["illrequests"], + "parameters": [{ + "name": "embed", + "in": "query", + "description": "Additional objects that should be embedded in the response", + "required": false, + "type": "array", + "collectionFormat": "csv", + "items": { + "type": "string", + "enum": [ + "patron", + "branch", + "capabilities" + ] + } + }, { + "name": "backend", + "in": "query", + "description": "The name of a ILL backend", + "required": false, + "type": "string" + }, { + "name": "orderid", + "in": "query", + "description": "The order ID of a request", + "required": false, + "type": "string" + }, { + "name": "biblio_id", + "in": "query", + "description": "The biblio ID associated with a request", + "required": false, + "type": "integer" + }, { + "name": "borrower_id", + "in": "query", + "description": "The borrower ID associated with a request", + "required": false, + "type": "integer" + }, { + "name": "completed", + "in": "query", + "description": "The date the request was considered completed", + "required": false, + "type": "string" + }, { + "name": "status", + "in": "query", + "description": "A full status string e.g. REQREV", + "required": false, + "type": "string" + }, { + "name": "medium", + "in": "query", + "description": "The medium of the requested item", + "required": false, + "type": "string" + }, { + "name": "updated", + "in": "query", + "description": "The last updated date of the request", + "required": false, + "type": "string" + }, { + "name": "placed", + "in": "query", + "description": "The date the request was placed", + "required": false, + "type": "string" + }, { + "name": "branch_id", + "in": "query", + "description": "The ID of the pickup branch", + "required": false, + "type": "string" + }], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK" + } + }, + "x-koha-authorization": { + "permissions": { + "borrowers": "1" + } + } + } + } +} diff --git a/etc/koha-conf.xml b/etc/koha-conf.xml index 4ccee3a..067e978 100644 --- a/etc/koha-conf.xml +++ b/etc/koha-conf.xml @@ -153,5 +153,26 @@ __PAZPAR2_TOGGLE_XML_POST__ 50 2 + + + __PERL_MODULE_DIR__/Koha/Illbackends + + hide + + hide + + branch + + ILLLIBS + + diff --git a/ill/ill-requests.pl b/ill/ill-requests.pl new file mode 100755 index 0000000..a267a8d --- /dev/null +++ b/ill/ill-requests.pl @@ -0,0 +1,238 @@ +#!/usr/bin/perl + +# Copyright 2013 PTFS-Europe Ltd and Mark Gavillet +# Copyright 2014 PTFS-Europe Ltd +# +# This file is part of Koha. +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use CGI; + +use C4::Auth; +use C4::Output; +use Koha::AuthorisedValues; +use Koha::Illrequests; +use Koha::Libraries; + +my $cgi = CGI->new; +my $illRequests = Koha::Illrequests->new; + +# Grab all passed data +# 'our' since Plack changes the scoping +# of 'my' +our $params = $cgi->Vars(); + +my $op = $params->{method} || 'illlist'; + +my ( $template, $patronnumber, $cookie ) = get_template_and_user( { + template_name => 'ill/ill-requests.tt', + query => $cgi, + type => 'intranet', + flagsrequired => { ill => '*' }, +} ); + +if ( $op eq 'illview' ) { + # View the details of an ILL + my $request = Koha::Illrequests->find($params->{illrequest_id}); + + $template->param( + request => $request + ); + +} elsif ( $op eq 'create' ) { + # We're in the process of creating a request + my $request = Koha::Illrequest->new + ->load_backend($params->{backend}); + my $backend_result = $request->backend_create($params); + $template->param( + whole => $backend_result, + request => $request + ); + handle_commit_maybe($backend_result, $request); + +} elsif ( $op eq 'confirm' ) { + # Backend 'confirm' method + # confirm requires a specific request, so first, find it. + my $request = Koha::Illrequests->find($params->{illrequest_id}); + my $backend_result = $request->backend_confirm($params); + $template->param( + whole => $backend_result, + request => $request, + ); + + # handle special commit rules & update type + handle_commit_maybe($backend_result, $request); + +} elsif ( $op eq 'cancel' ) { + # Backend 'cancel' method + # cancel requires a specific request, so first, find it. + my $request = Koha::Illrequests->find($params->{illrequest_id}); + my $backend_result = $request->backend_cancel($params); + $template->param( + whole => $backend_result, + request => $request, + ); + + # handle special commit rules & update type + handle_commit_maybe($backend_result, $request); + +} elsif ( $op eq 'edit_action' ) { + # Handle edits to the Illrequest object. + # (not the Illrequestattributes) + # We simulate the API for backend requests for uniformity. + # So, init: + my $request = Koha::Illrequests->find($params->{illrequest_id}); + if ( !$params->{stage} ) { + my $backend_result = { + error => 0, + status => '', + message => '', + method => 'edit_action', + stage => 'init', + next => '', + value => {} + }; + $template->param( + whole => $backend_result, + request => $request + ); + } else { + # Commit: + # Save the changes + $request->borrowernumber($params->{borrowernumber}); + $request->biblio_id($params->{biblio_id}); + $request->branchcode($params->{branchcode}); + $request->notesopac($params->{notesopac}); + $request->notesstaff($params->{notesstaff}); + $request->store; + my $backend_result = { + error => 0, + status => '', + message => '', + method => 'edit_action', + stage => 'commit', + next => 'illlist', + value => {} + }; + handle_commit_maybe($backend_result, $request); + } + +} elsif ( $op eq 'moderate_action' ) { + # Moderate action is required for an ILL submodule / syspref. + # Currently still needs to be implemented. + redirect_to_list(); + +} elsif ( $op eq 'delete_confirm') { + my $request = Koha::Illrequests->find($params->{illrequest_id}); + + $template->param( + request => $request + ); + +} elsif ( $op eq 'delete' ) { + + # Check if the request is confirmed, if not, redirect + # to the confirmation view + if ($params->{confirmed} == 1) { + # We simply delete the request... + my $request = Koha::Illrequests->find( + $params->{illrequest_id} + )->delete; + # ... then return to list view. + redirect_to_list(); + } else { + print $cgi->redirect( + "/cgi-bin/koha/ill/ill-requests.pl?" . + "method=delete_confirm&illrequest_id=" . + $params->{illrequest_id}); + } + +} elsif ( $op eq 'generic_confirm' ) { + my $request = Koha::Illrequests->find($params->{illrequest_id}); + $params->{current_branchcode} = C4::Context->mybranch; + my $backend_result = $request->generic_confirm($params); + $template->param( + whole => $backend_result, + request => $request, + ); + + # handle special commit rules & update type + handle_commit_maybe($backend_result, $request); + +} elsif ( $op eq 'illlist') { + # Display all current ILLs + my $requests = $illRequests->search(); + + $template->param( + requests => $requests + ); + + # If we receive a pre-filter, make it available to the template + my $possible_filters = ['borrowernumber']; + my $active_filters = []; + foreach my $filter(@{$possible_filters}) { + if ($params->{$filter}) { + push @{$active_filters}, + { name => $filter, value => $params->{$filter}}; + } + } + if (scalar @{$active_filters} > 0) { + $template->param( + prefilters => $active_filters + ); + } +} else { + my $request = Koha::Illrequests->find($params->{illrequest_id}); + my $backend_result = $request->custom_capability($op, $params); + $template->param( + whole => $backend_result, + request => $request, + ); +} + +# Get a list of backends +my $ir = Koha::Illrequest->new; + +$template->param( + backends => $ir->available_backends, + media => [ "Book", "Article", "Journal" ], + query_type => $op, + branches => Koha::Libraries->search->unblessed, + here_link => "/cgi-bin/koha/ill/ill-requests.pl" +); + +output_html_with_http_headers( $cgi, $cookie, $template->output ); + +sub handle_commit_maybe { + my ( $backend_result, $request ) = @_; + # We need to special case 'commit' + if ( $backend_result->{stage} eq 'commit' ) { + if ( $backend_result->{next} eq 'illview' ) { + # Redirect to a view of the newly created request + print $cgi->redirect( + '/cgi-bin/koha/ill/ill-requests.pl?method=illview&illrequest_id='. + $request->id + ); + } else { + # Redirect to a requests list view + redirect_to_list(); + } + } +} + +sub redirect_to_list { + print $cgi->redirect('/cgi-bin/koha/ill/ill-requests.pl'); +} diff --git a/koha-tmpl/intranet-tmpl/prog/css/staff-global.css b/koha-tmpl/intranet-tmpl/prog/css/staff-global.css index 784fc3e..e86043d 100644 --- a/koha-tmpl/intranet-tmpl/prog/css/staff-global.css +++ b/koha-tmpl/intranet-tmpl/prog/css/staff-global.css @@ -3019,3 +3019,80 @@ fieldset.rows + fieldset.action { #patron_search #filters { display: none; } + +#interlibraryloans h1 { + margin: 1em 0; +} + +#interlibraryloans h2 { + margin-bottom: 20px; +} + +#interlibraryloans h3 { + margin-top: 20px; +} + +#interlibraryloans .bg-info { + overflow: auto; + position: relative; +} + +#interlibraryloans #search-summary { + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + transform: translateY(-50%); + position: absolute; + top: 50%; +} + +#interlibraryloans .format h5 { + margin-top: 20px; +} + +#interlibraryloans .format li { + list-style: none; +} + +#interlibraryloans .format h4 { + margin-bottom: 20px; +} + +#interlibraryloans .format input { + margin: 10px 0; +} + +#interlibraryloans #freeform-fields .custom-name { + width: 9em; + margin-right: 1em; + text-align: right; +} + +#interlibraryloans #freeform-fields .delete-new-field { + margin-left: 1em; +} + +#interlibraryloans #add-new-fields { + margin: 1em; +} + +#ill-view-panel { + margin-top: 15px; +} + +#ill-view-panel h3 { + margin-bottom: 10px; +} + +#ill-view-panel h4 { + margin-bottom: 20px; +} + +#ill-view-panel .rows div { + height: 1em; + margin-bottom: 1em; +} + +#ill-view-panel #requestattributes .label { + width: auto; +} diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc index e621d64..4006f6c 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc @@ -112,5 +112,8 @@ [% IF Koha.Preference('HouseboundModule') %] [% IF houseboundview %]
  • [% ELSE %]
  • [% END %]Housebound
  • [% END %] + [% IF Koha.Preference('ILLModule') %] +
  • Interlibrary loans
  • + [% END %] [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/ill-toolbar.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/ill-toolbar.inc new file mode 100644 index 0000000..3f541d9 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/ill-toolbar.inc @@ -0,0 +1,24 @@ +[% USE Koha %] +[% IF Koha.Preference('ILLModule ') %] +
    + [% IF backends.size > 1 %] + + [% ELSE %] + + New ILL request + + [% END %] + + List requests + +
    +[% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc index 7c6275a..4a1ce81 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc @@ -20,6 +20,7 @@ [%- CASE 'plugins' -%]Koha plugins [%- CASE 'lists' -%]Lists [%- CASE 'clubs' -%]Patron clubs + [%- CASE 'ill' -%]Create and modify Interlibrary loan requests [%- END -%] [%- END -%] 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 new file mode 100644 index 0000000..201a977 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt @@ -0,0 +1,487 @@ +[% USE Branches %] + +[% INCLUDE 'doc-head-open.inc' %] +Koha › ILL requests › +[% INCLUDE 'doc-head-close.inc' %] + + + +[% INCLUDE 'datatables.inc' %] + + + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'cat-search.inc' %] + + + +
    +
    +
    +
    + [% INCLUDE 'ill-toolbar.inc' %] + + [% IF whole.error %] +

    Error performing operation

    + +

    We encountered an error:

    +
      +
    1. [% whole.status %]
    2. +
    3. [% whole.message %]
    4. +
    + [% END %] + + [% IF query_type == 'create' %] +

    New ILL request

    + [% PROCESS $whole.template %] + + [% ELSIF query_type == 'confirm' %] +

    Confirm ILL request

    + [% PROCESS $whole.template %] + + [% ELSIF query_type == 'cancel' %] +

    Cancel an confirmed request

    + [% PROCESS $whole.template %] + + [% ELSIF query_type == 'generic_confirm' %] +

    Place request with partner libraries

    + + [% IF whole.value.partners %] + [% ill_url = here_link _ "?method=illview&illrequest_id=" _ request.illrequest_id %] +
    +
    + Interlibrary loan request details +
      +
    1. + + +
    2. +
    3. + + + +
    4. +
    5. + + +
    6. +
    7. + + +
    8. +
    + + + +
    +
    + + Cancel +
    +
    + [% ELSE %] +
    + Interlibrary loan request details +

    No partners have been defined yet. Please create appropriate patron records (by default ILLLIBS category).

    +

    Be sure to provide email addresses for these patrons.

    +

    Cancel

    +
    + [% END %] + + + [% ELSIF query_type == 'edit_action' %] +
    +
    + Request details +
      +
    1. + + +
    2. +
    3. + + +
    4. +
    5. + + +
    6. +
    7. + + [% stat = request.status %] + [% request.capabilities.$stat.name %] +
    8. +
    9. + + [% request.updated %] +
    10. +
    11. + + [% request.medium %] +
    12. +
    13. + + [% request.cost %] +
    14. +
    15. + + [% request.id_prefix _ request.illrequest_id %] +
    16. +
    17. + + +
    18. +
    19. + + +
    20. +
    +
    +
    + + + + + Cancel +
    +
    + + [% ELSIF query_type == 'delete_confirm' %] + +
    +

    Are you sure you wish to delete this request?

    +

    + Yes + No +

    +
    + + + [% ELSIF query_type == 'illview' %] + [% actions = request.available_actions %] + [% capabilities = request.capabilities %] + [% req_status = request.status %] +

    Manage ILL request

    +
    + + + Edit request + + [% FOREACH action IN actions %] + [% IF action.method != 0 %] + + + [% action.ui_method_name %] + + [% END %] + [% END %] +
    +
    +
    +

    Request details

    +
    +
    +

    Details from library

    +
    +
    + Order ID: + [% request.orderid || "N/A" %] +
    +
    + Borrower: + [% borrowerlink = "/cgi-bin/koha/members/moremember.pl" + _ "?borrowernumber=" _ request.patron.borrowernumber %] + + [% request.patron.firstname _ " " + _ request.patron.surname _ " [" + _ request.patron.cardnumber + _ "]" %] + +
    + +
    + Biblio number: + [% request.biblio_id || "N/A" %] +
    +
    + Branch: + [% Branches.GetName(request.branchcode) %] +
    +
    + Status: + [% capabilities.$req_status.name %] +
    +
    + Last updated: + [% request.updated %] +
    +
    + Request type: + [% request.medium %] +
    +
    + Cost: + [% request.cost || "N/A" %] +
    +
    + Request number: + [% request.id_prefix _ request.illrequest_id %] +
    +
    + Staff notes: +
    [% request.notesstaff %]
    +
    +
    + Notes: +
    [% request.notesopac %]
    +
    +
    +
    +

    Details from supplier ([% request.backend %])

    + [% FOREACH meta IN request.metadata %] +
    + [% meta.key %]: + [% meta.value %] +
    + [% END %] +
    +
    +

    Toggle full supplier metadata

    +
    + [% FOREACH attr IN request.illrequestattributes %] +
    + [% attr.type %]: + [% attr.value %] +
    + [% END %] +
    + +
    +
    +
    + + [% ELSIF query_type == 'illlist' %] + +

    View ILL requests

    +
    +

    Details for all requests

    + + + + + + + + + + + + + + + + + + + + +
    Order ID:Author:Title:Borrower:Borrower number:Biblio number:Branch:Status:Last updated:Request type:Cost:Request number:Manage request:
    +
    + [% ELSE %] + + [% INCLUDE $whole.template %] + + [% END %] +
    +
    +
    +
    + +[% TRY %] +[% PROCESS backend_jsinclude %] +[% CATCH %] +[% END %] + +[% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt index 2a69dd9..32a1409 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt @@ -63,6 +63,11 @@
  • Authorities
  • + [% IF Koha.Preference('ILLModule') %] +
  • + ILL requests +
  • + [% END %] diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc index 9afa331..7d27f22 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc +++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc @@ -104,6 +104,15 @@ [% END %] ask for a discharge [% END %] + + [% IF Koha.Preference( 'ILLModule' ) == 1 %] + [% IF ( illrequestsview ) %] +
  • + [% ELSE %] +
  • + [% END %] + your interlibrary loan requests
  • + [% END %] [% END %] diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-illrequests.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-illrequests.tt new file mode 100644 index 0000000..0c4600f --- /dev/null +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-illrequests.tt @@ -0,0 +1,219 @@ +[% USE Koha %] +[% USE Branches %] +[% INCLUDE 'doc-head-open.inc' %] +[% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog › Your Interlibrary loan requests[% INCLUDE 'doc-head-close.inc' %] +[% BLOCK cssinclude %][% END %] + +[% INCLUDE 'bodytag.inc' bodyid='opac-illrequests' bodyclass='scrollto' %] +[% BLOCK messages %] + [% IF message == "1" %] + + [% ELSIF message == "2" %] + + [% END %] +[% END %] +[% INCLUDE 'masthead.inc' %] +
    + + +
    +
    + [% IF ( OpacNav||loggedinusername ) && !print %] +
    + +
    + [% END %] + + [% IF ( OpacNav||loggedinusername ) %] +
    + [% ELSE %] +
    + [% END %] +
    + [% IF method == 'create' %] +

    New Interlibrary loan request

    + [% INCLUDE messages %] + [% IF backends %] +
    +
    + + +
    +
    + + +
    +
    + [% ELSE %] + [% PROCESS $whole.opac_template %] + [% END %] + [% ELSIF method == 'list' %] +

    Interlibrary loan requests

    + [% INCLUDE messages %] + + + + + + + + + + + + + + + + + [% FOREACH request IN requests %] + [% status = request.status %] + + + + + + + + + + [% END %] + +
    AuthorTitleRequested fromRequest typeStatusLast updated
    [% request.metadata.Author || 'N/A' %][% request.metadata.Title || 'N/A' %][% request.backend %][% request.medium %][% request.capabilities.$status.name %][% request.updated %] + View +
    + [% ELSIF method == 'view' %] +

    View Interlibrary loan request

    + [% INCLUDE messages %] + [% status = request.status %] +
    +
    + Details from library +
      +
    1. + + [% request.backend %] +
    2. + [% IF request.biblio_id %] +
    3. + + Click here to view +
    4. + [% END %] +
    5. + + [% Branches.GetName(request.branchcode) %] +
    6. +
    7. + + [% request.capabilities.$status.name %] +
    8. +
    9. + + [% request.medium %] +
    10. +
    11. + + [% request.placed %] +
    12. +
    13. + + [% request.updated %] +
    14. +
    15. + + [% IF !request.completed %] + + [% ELSE %] + [% request.notesopac %] + [% END %] +
    16. +
    +
    +
    + Details from [% request.backend %] + [% FOREACH meta IN request.metadata %] +
    + [% meta.key %]: + [% meta.value || 'N/A' %] +
    + [% END %] +
    +
    + + + [% IF !request.completed %] + [% IF request.status != "CANCREQ" %] + Request cancellation + [% END %] + + [% END %] + Cancel +
    +
    + [% END %] +
    +
    +
    +
    +
    + +[% INCLUDE 'opac-bottom.inc' %] + +[% BLOCK jsinclude %] +[% INCLUDE 'datatables.inc' %] + +[% TRY %] +[% PROCESS backend_jsinclude %] +[% CATCH %] +[% END %] +[% END %] diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results-grouped.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results-grouped.tt index 8595f9a..eaa20b3 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results-grouped.tt +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results-grouped.tt @@ -253,14 +253,30 @@ href="/cgi-bin/koha/opac-rss.pl?[% query_cgi %][% limit_cgi |html %]" /> [% INCLUDE 'page-numbers.inc' %] [% END # / IF total %] - [% IF Koha.Preference( 'suggestion' ) == 1 %] - [% IF Koha.Preference( 'AnonSuggestions' ) == 1 %] -
    Not finding what you're looking for?
    Make a purchase suggestion
    - [% ELSE %] - [% IF ( loggedinusername ) %]
    Not finding what you're looking for?
    Make a purchase suggestion
    [% END %] - [% END %] + [% IF + Koha.Preference( 'suggestion' ) == 1 && + ( + Koha.Preference( 'AnonSuggestions' ) == 1 || + loggedinusername || + Koha.Preference( 'ILLModule' ) == 1 + ) + %] +
    + Not finding what you're looking for? + +
    [% END %] -
    diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results.tt index a5a7c70..11cf98e 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results.tt +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results.tt @@ -562,16 +562,29 @@ [% END # / IF total %] - [% IF Koha.Preference( 'suggestion' ) == 1 %] - [% IF Koha.Preference( 'AnonSuggestions' ) == 1 %] -
    Not finding what you're looking for?
    Make a purchase suggestion
    - [% ELSE %] - [% IF ( loggedinusername ) %] -
    - Not finding what you're looking for?
    Make a purchase suggestion -
    - [% END %] - [% END %] + [% IF + Koha.Preference( 'suggestion' ) == 1 && + ( + Koha.Preference( 'AnonSuggestions' ) == 1 || + loggedinusername || + Koha.Preference( 'ILLModule' ) == 1 + ) + %] +
    + Not finding what you're looking for? + +
    [% END %] diff --git a/koha-tmpl/opac-tmpl/bootstrap/less/opac.less b/koha-tmpl/opac-tmpl/bootstrap/less/opac.less index 63961e8..c4e55c2 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/less/opac.less +++ b/koha-tmpl/opac-tmpl/bootstrap/less/opac.less @@ -2508,6 +2508,44 @@ a.reviewlink:visited { font-size: 90%; } +#illrequests { + .illrequest-actions { + .btn, + .cancel { + margin-right: 5px; + } + padding-top: 20px; + margin-bottom: 20px; + } + #illrequests-create-button { + margin-bottom: 20px; + } + .bg-info { + overflow: auto; + position: relative; + } + .bg-info { + #search-summary { + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + transform: translateY(-50%); + position: absolute; + top: 50%; + } + + } + #freeform-fields .custom-name { + float: left; + width: 8em; + margin-right: 1em; + text-align: right; + } + .dropdown:hover .dropdown-menu.nojs { + display: block; + } +} + #dc_fieldset { border: 1px solid #dddddd; border-width: 1px; diff --git a/opac/opac-illrequests.pl b/opac/opac-illrequests.pl new file mode 100755 index 0000000..9ff413a --- /dev/null +++ b/opac/opac-illrequests.pl @@ -0,0 +1,129 @@ +#!/usr/bin/perl + +# Copyright 2017 PTFS-Europe Ltd +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use CGI qw ( -utf8 ); +use C4::Auth; +use C4::Koha; +use C4::Output; + +use Koha::Illrequests; +use Koha::Libraries; +use Koha::Patrons; + +my $query = new CGI; + +# Grab all passed data +# 'our' since Plack changes the scoping +# of 'my' +our $params = $query->Vars(); + +# if illrequests is disabled, leave immediately +if ( ! C4::Context->preference('ILLModule') ) { + print $query->redirect("/cgi-bin/koha/errors/404.pl"); + exit; +} + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user({ + template_name => "opac-illrequests.tt", + query => $query, + type => "opac", + authnotrequired => ( C4::Context->preference("OpacPublic") ? 1 : 0 ), +}); + +my $op = $params->{'method'} || 'list'; + +if ( $op eq 'list' ) { + + my $requests = Koha::Illrequests->search( + { borrowernumber => $loggedinuser } + ); + my $req = Koha::Illrequest->new; + $template->param( + requests => $requests, + backends => $req->available_backends + ); + +} elsif ( $op eq 'view') { + my $request = Koha::Illrequests->find({ + borrowernumber => $loggedinuser, + illrequest_id => $params->{illrequest_id} + }); + $template->param( + request => $request + ); + +} elsif ( $op eq 'update') { + my $request = Koha::Illrequests->find({ + borrowernumber => $loggedinuser, + illrequest_id => $params->{illrequest_id} + }); + $request->notesopac($params->{notesopac})->store; + print $query->redirect( + '/cgi-bin/koha/opac-illrequests.pl?method=view&illrequest_id=' . + $params->{illrequest_id} . + '&message=1' + ); +} elsif ( $op eq 'cancreq') { + my $request = Koha::Illrequests->find({ + borrowernumber => $loggedinuser, + illrequest_id => $params->{illrequest_id} + }); + $request->status('CANCREQ')->store; + print $query->redirect( + '/cgi-bin/koha/opac-illrequests.pl?method=view&illrequest_id=' . + $params->{illrequest_id} . + '&message=1' + ); + +} elsif ( $op eq 'create' ) { + if (!$params->{backend}) { + my $req = Koha::Illrequest->new; + $template->param( + backends => $req->available_backends + ); + } else { + my $request = Koha::Illrequest->new + ->load_backend($params->{backend}); + $params->{cardnumber} = Koha::Patrons->find({ + borrowernumber => $loggedinuser + })->cardnumber; + my $backend_result = $request->backend_create($params); + $template->param( + media => [ "Book", "Article", "Journal" ], + branches => Koha::Libraries->search->unblessed, + whole => $backend_result, + request => $request + ); + if ($backend_result->{stage} eq 'commit') { + print $query->redirect('/cgi-bin/koha/opac-illrequests.pl?message=2'); + } + } + + +} + +$template->param( + message => $params->{message}, + illrequestsview => 1, + method => $op +); + +output_html_with_http_headers $query, $cookie, $template->output; diff --git a/t/db_dependent/Illrequest.t b/t/db_dependent/Illrequest.t new file mode 100644 index 0000000..5fd5f0b --- /dev/null +++ b/t/db_dependent/Illrequest.t @@ -0,0 +1,544 @@ +#!/usr/bin/perl +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +use Modern::Perl; + +use File::Basename qw/basename/; +use Koha::Database; +use Koha::Illrequestattributes; +use Koha::Patrons; +use t::lib::TestBuilder; + +use Test::More tests => 44; + +# We want to test the Koha IllRequest object. At its core it's a simple +# Koha::Object, mapping to the ill_request table. +# +# This object will supersede the Status object in ILLModule. +# +# We must ensure perfect backward compatibility between the current model and +# the Status less model. + +use_ok('Koha::Illrequest'); +use_ok('Koha::Illrequests'); + +my $schema = Koha::Database->new->schema; +$schema->storage->txn_begin; + +my $builder = t::lib::TestBuilder->new; + +my $patron = $builder->build({ source => 'Borrower' }); +my $branch = $builder->build({ source => 'Branch' }); + +my $illRequest = $builder->build({ + source => 'Illrequest', + value => { + borrowernumber => $patron->{borrowernumber}, + branch => $branch->{branchcode}, + biblionumber => 0, + status => 'NEW', + completion_date => 0, + reqtype => 'book', + } +}); + +my $illObject = Koha::Illrequests->find($illRequest->{illrequest_id}); + +isa_ok($illObject, "Koha::Illrequest"); + +# Test delete works correctly. +my $illRequestDelete = $builder->build({ + source => 'Illrequest', + value => { + borrowernumber => $patron->{borrowernumber}, + branch => $branch->{branchcode}, + biblionumber => 0, + status => 'NEW', + completion_date => 0, + reqtype => 'book', + } +}); +sub ill_req_search { + return Koha::Illrequestattributes->search({ + illrequest_id => $illRequestDelete->{illrequest_id} + })->count; +} + +is(ill_req_search, 0, "Correctly not found matching Illrequestattributes."); +# XXX: For some reason test builder can't build Illrequestattributes. +my $illReqAttr = Koha::Illrequestattribute->new({ + illrequest_id => $illRequestDelete->{illrequest_id}, + type => "test", + value => "Hello World" +})->store; +is(ill_req_search, 1, "We have found a matching Illrequestattribute."); + +Koha::Illrequests->find($illRequestDelete->{illrequest_id})->delete; +is( + Koha::Illrequests->find($illRequestDelete->{illrequest_id}), + undef, + "Correctly deleted Illrequest." +); +is(ill_req_search, 0, "Correctly deleted Illrequestattributes."); + +# Test Accessing of related records. + +# # TODO the conclusion from being able to use one_to_many? we no longer need +# # the Record object: simply pass the `ill_request_attributes` resultset +# # whenever one would pass a Record. + +my $illRequest2 = $builder->build({ + source => 'Illrequest', + value => { + borrower_id => $patron->{borrowernumber}, + branch_id => $branch->{branchcode}, + biblio_id => 0, + status => 'NEW', + completed => 0, + medium => 'book', + } +}); +my $illReqAttr2 = Koha::Illrequestattribute->new({ + illrequest_id => $illRequest2->{illrequest_id}, + type => "test2", + value => "Hello World" +})->store; +my $illReqAttr3 = Koha::Illrequestattribute->new({ + illrequest_id => $illRequest2->{illrequest_id}, + type => "test3", + value => "Hello Space" +})->store; + +my $illRequestAttributes = Koha::Illrequests + ->find($illRequest2->{illrequest_id})->illrequestattributes; + +isa_ok($illRequestAttributes, "Koha::Illrequestattributes"); + +is($illRequestAttributes->count, 2, "Able to search related."); + +# Test loading of 'Config'. + +my $rqConfigTest = Koha::Illrequest->new({ + borrower_id => $patron->{borrowernumber}, + branch_id => $branch->{branchcode}, +}); + +isa_ok($rqConfigTest->_config, "Koha::Illrequest::Config"); + +# Test loading of 'Dummy' backend. + +my $rqBackendTest = Koha::Illrequest->new({ + borrower_id => $patron->{borrowernumber}, + branch_id => $branch->{branchcode}, +})->store; + +$rqBackendTest->_config->backend("Dummy"); +$rqBackendTest->_config->limits({ default => { count => -1 } }); +isa_ok($rqBackendTest->_backend, "Koha::Illbackends::Dummy::Base"); + +# Test use of 'Dummy' Backend. + +## Test backend_update_status + +# FIXME: This breaks transparancy of ->status method! +eval { $rqBackendTest->status("ERR") }; +ok($@, "status: Test for status error on hook fail."); + +# FIXME: Will need to test this on new illRequest to not pollute rest of +# tests. + +# is($rqBackendTest->status("NEW")->status, "NEW", "status: Setter works +# OK."); +# is($rqBackendTest->status(null), null, "status: Unsetter works OK."); + +## Test backend_create + +is( + $rqBackendTest->status, + undef, + "backend_create: Test our status initiates correctly." +); + +# Request a search form +my $created_rq = $rqBackendTest->backend_create({ + stage => "search_form", + method => "create", +}); + +is( $created_rq->{stage}, 'search_results', + "backend_create: search_results stage." ); + +# Request search results +# FIXME: fails because of missing patron / branch info. +# $created_rq = $rqBackendTest->backend_create({ +# stage => "search_results", +# method => "create", +# other => { search => "interlibrary loans" }, +# }); + +# is_deeply( +# $created_rq, +# { +# error => 0, +# status => '', +# message => '', +# method => 'create', +# stage => 'search_results', +# template => 'ill/Dummy/create.inc', +# value => [ +# { +# id => 1234, +# title => "Ordering ILLs using Koha", +# author => "A.N. Other", +# }, +# { +# id => 5678, +# title => "Interlibrary loans in Koha", +# author => "A.N. Other", +# }, +# ], +# } +# , +# "backend_create: search_results stage." +# ); + +# # Create the request +# $created_rq = $rqBackendTest->backend_create({ +# stage => "commit", +# method => "create", +# other => { +# id => 1234, +# title => "Ordering ILLs using Koha", +# author => "A.N. Other", +# }, +# }); + +# while ( my ( $field, $value ) = each %{$created_rq} ) { +# isnt($value, undef, "backend_create: key '$field' exists"); +# }; + +# is( +# $rqBackendTest->status, +# "NEW", +# "backend_create: Test our status was updated." +# ); + +# cmp_ok( +# $rqBackendTest->illrequestattributes->count, +# "==", +# 3, +# "backend_create: Ensure we have correctly stored our new attributes." +# ); + +# ## Test backend_list + +# is_deeply( +# $rqBackendTest->backend_list->{value}, +# { +# 1 => { +# id => 1234, +# title => "Ordering ILLs using Koha", +# author => "A.N. Other", +# status => "On order", +# cost => "30 GBP", +# } +# }, +# "backend_list: Retrieve our list of requested requests." +# ); + +# ## Test backend_renew + +# ok( +# $rqBackendTest->backend_renew->{error}, +# "backend_renew: Error for invalid request." +# ); +# is_deeply( +# $rqBackendTest->backend_renew->{value}, +# { +# id => 1234, +# title => "Ordering ILLs using Koha", +# author => "A.N. Other", +# }, +# "backend_renew: Renew request." +# ); + +# ## Test backend_confirm + +# my $rqBackendTestConfirmed = $rqBackendTest->backend_confirm; +# is( +# $rqBackendTest->status, +# "REQ", +# "backend_commit: Confirm status update correct." +# ); +# is( +# $rqBackendTest->orderid, +# 1234, +# "backend_commit: Confirm orderid populated correctly." +# ); + +# ## Test backend_status + +# is( +# $rqBackendTest->backend_status->{error}, +# 0, +# "backend_status: error for invalid request." +# ); +# is_deeply( +# $rqBackendTest->backend_status->{value}, +# { +# id => 1234, +# status => "On order", +# title => "Ordering ILLs using Koha", +# author => "A.N. Other", +# }, +# "backend_status: Retrieve the status of request." +# ); + +# # Now test trying to get status on non-confirmed request. +my $rqBackendTestUnconfirmed = Koha::Illrequest->new({ + borrower_id => $patron->{borrowernumber}, + branch_id => $branch->{branchcode}, +})->store; +$rqBackendTestUnconfirmed->_config->backend("Dummy"); +$rqBackendTestUnconfirmed->_config->limits({ default => { count => -1 } }); + +$rqBackendTestUnconfirmed->backend_create({ + stage => "commit", + method => "create", + other => { + id => 1234, + title => "Ordering ILLs using Koha", + author => "A.N. Other", + }, +}); +is( + $rqBackendTestUnconfirmed->backend_status->{error}, + 1, + "backend_status: error for invalid request." +); + +## Test backend_cancel + +# is( +# $rqBackendTest->backend_cancel->{error}, +# 0, +# "backend_cancel: Successfully cancelling request." +# ); +# is_deeply( +# $rqBackendTest->backend_cancel->{value}, +# { +# id => 1234, +# title => "Ordering ILLs using Koha", +# author => "A.N. Other", +# }, +# "backend_cancel: Cancel request." +# ); + +# Now test trying to cancel non-confirmed request. +is( + $rqBackendTestUnconfirmed->backend_cancel->{error}, + 1, + "backend_cancel: error for invalid request." +); +is_deeply( + $rqBackendTestUnconfirmed->backend_cancel->{value}, + {}, + "backend_cancel: Cancel request." +); + +# Test Helpers + +## Test getCensorNotesStaff + +is($rqBackendTest->getCensorNotesStaff, 1, "getCensorNotesStaff: Public."); +$rqBackendTest->_config->censorship({ + censor_notes_staff => 0, + censor_reply_date => 0, +}); +is($rqBackendTest->getCensorNotesStaff, 0, "getCensorNotesStaff: Censored."); + +## Test getCensorNotesStaff + +is($rqBackendTest->getDisplayReplyDate, 1, "getDisplayReplyDate: Yes."); +$rqBackendTest->_config->censorship({ + censor_notes_staff => 0, + censor_reply_date => 1, +}); +is($rqBackendTest->getDisplayReplyDate, 0, "getDisplayReplyDate: No."); + +# FIXME: These should be handled by the templates. +# # Test Output Helpers + +# ## Test getStatusSummary + +# $rqBackendTest->medium("Book")->store; +# is_deeply( +# $rqBackendTest->getStatusSummary({brw => 0}), +# { +# biblionumber => ["Biblio Number", undef], +# borrowernumber => ["Borrower Number", $patron->{borrowernumber}], +# id => ["Request Number", $rqBackendTest->illrequest_id], +# prefix_id => ["Request Number", $rqBackendTest->illrequest_id], +# reqtype => ["Request Type", "Book"], +# status => ["Status", "REQREV"], +# }, +# "getStatusSummary: Without Borrower." +# ); + +# is_deeply( +# $rqBackendTest->getStatusSummary({brw => 1}), +# { +# biblionumber => ["Biblio Number", undef], +# borrower => ["Borrower", Koha::Patrons->find($patron->{borrowernumber})], +# id => ["Request Number", $rqBackendTest->illrequest_id], +# prefix_id => ["Request Number", $rqBackendTest->illrequest_id], +# reqtype => ["Request Type", "Book"], +# status => ["Status", "REQREV"], +# }, +# "getStatusSummary: With Borrower." +# ); + +# ## Test getFullStatus + +# is_deeply( +# $rqBackendTest->getFullStatus({brw => 0}), +# { +# biblionumber => ["Biblio Number", undef], +# borrowernumber => ["Borrower Number", $patron->{borrowernumber}], +# id => ["Request Number", $rqBackendTest->illrequest_id], +# prefix_id => ["Request Number", $rqBackendTest->illrequest_id], +# reqtype => ["Request Type", "Book"], +# status => ["Status", "REQREV"], +# placement_date => ["Placement Date", $rqBackendTest->placed], +# completion_date => ["Completion Date", $rqBackendTest->completed], +# ts => ["Timestamp", $rqBackendTest->updated], +# branch => ["Branch", $rqBackendTest->branch_id], +# }, +# "getFullStatus: Without Borrower." +# ); + +# is_deeply( +# $rqBackendTest->getFullStatus({brw => 1}), +# { +# biblionumber => ["Biblio Number", undef], +# borrower => ["Borrower", Koha::Patrons->find($patron->{borrowernumber})], +# id => ["Request Number", $rqBackendTest->illrequest_id], +# prefix_id => ["Request Number", $rqBackendTest->illrequest_id], +# reqtype => ["Request Type", "Book"], +# status => ["Status", "REQREV"], +# placement_date => ["Placement Date", $rqBackendTest->placed], +# completion_date => ["Completion Date", $rqBackendTest->completed], +# ts => ["Timestamp", $rqBackendTest->updated], +# branch => ["Branch", $rqBackendTest->branch_id], +# }, +# "getFullStatus: With Borrower." +# ); + +## Test available_backends +subtest 'available_backends' => sub { + plan tests => 1; + + my $rq = Koha::Illrequest->new({ + borrower_id => $patron->{borrowernumber}, + branch_id => $branch->{branchcode}, + })->store; + + my @backends = (); + my $backenddir = $rq->_config->backend_dir; + @backends = <$backenddir/*> if ( $backenddir ); + @backends = map { basename($_) } @backends; + is_deeply(\@backends, $rq->available_backends, + "Correctly identify available backends."); + +}; + +## Test capabilities + +my $rqCapTest = Koha::Illrequest->new({ + borrower_id => $patron->{borrowernumber}, + branch_id => $branch->{branchcode}, +})->store; + +is( keys %{$rqCapTest->_core_status_graph}, + @{[ 'NEW', 'REQ', 'REVREQ', 'QUEUED', 'CANCREQ', 'COMP', 'KILL' ]}, + "Complete list of core statuses." ); + +my $union = $rqCapTest->_status_graph_union( + $rqCapTest->_core_status_graph, + { + TEST => { + prev_actions => [ 'COMP' ], + id => 'TEST', + name => "Test", + ui_method_name => "Perform test", + method => 'test', + next_actions => [ 'NEW' ] + }, + BLAH => { + prev_actions => [ 'COMP' ], + id => 'BLAH', + name => "BLAH", + ui_method_name => "Perform test", + method => 'test', + next_actions => [ 'NEW' ] + }, + } +); +ok( ( grep 'BLAH', @{$union->{COMP}->{next_actions}} and + grep 'TEST', @{$union->{COMP}->{next_actions}} ), + "next_actions: updated." ); +ok( ( grep 'BLAH', @{$union->{NEW}->{prev_actions}} and + grep 'TEST', @{$union->{NEW}->{prev_actions}} ), + "next_actions: updated." ); + +## Test available_backends +subtest 'available_actions' => sub { + plan tests => 1; + + my $rq = Koha::Illrequest->new({ + borrower_id => $patron->{borrowernumber}, + branch_id => $branch->{branchcode}, + status => 'NEW', + })->store; + + is_deeply( + $rq->available_actions, + [ + { + prev_actions => [ 'NEW', 'REQREV', 'QUEUED' ], + id => 'REQ', + name => 'Requested', + ui_method_name => 'Create request', + method => 'confirm', + next_actions => [ 'REQREV' ], + }, + { + prev_actions => [ 'CANCREQ', 'QUEUED', 'REQREV', 'NEW' ], + id => 'KILL', + name => 0, + ui_method_name => 'Delete request', + method => 'delete', + next_actions => [ ], + } + ] + ); +}; + +$schema->storage->txn_rollback; + +1; diff --git a/t/db_dependent/Illrequest/Config.t b/t/db_dependent/Illrequest/Config.t new file mode 100644 index 0000000..df2bcac --- /dev/null +++ b/t/db_dependent/Illrequest/Config.t @@ -0,0 +1,344 @@ +#!/usr/bin/perl + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use Test::Exception; +use Test::More; +use Test::Warn; + +# Some data structures that will be repeatedly referenced +my $defaults = { + api_key => "564euie", + api_auth => "unidaenudvnled", +}; +my $application = { + key => "6546uedrun", + auth => "edutrineadue", +}; +# Simulate $from_xml +my $params = { + application => $application, + backend => "Dummy", + configuration => $defaults, + reply_date => "hide", + staff_request_comments => "hide", +}; +my $first_branch = { + code => "test", api_key => "dphügnpgüffq", api_auth => "udrend" +}; +my $second_branch = { + code => "second", + api_key => "eduirn", + api_auth => "eudtireand", + request_limit => { count => "5" }, +}; + +BEGIN { + use_ok('Koha::Illrequest::Config'); +} + +my $config = Koha::Illrequest::Config->new(1); # with test_mode enabled. +isa_ok($config, 'Koha::Illrequest::Config'); + +# _load_configuration +is_deeply( + Koha::Illrequest::Config::_load_configuration($params), + { + api_url => 'http://apitest.bldss.bl.uk', + backend => 'Dummy', + censorship => { + censor_notes_staff => 1, + censor_reply_date => 1, + }, + credentials => { + api_application => $application, + api_keys => { default => $defaults }, + }, + default_formats => {}, + digital_recipients => {}, + library_privileges => {}, + limits => {}, + prefixes => {}, + spec_file => undef, + }, + "Basic _load_configuration" +); + +$params->{configuration}->{request_limit}->{count} = 10; +is_deeply( + Koha::Illrequest::Config::_load_configuration($params), + { + api_url => 'http://apitest.bldss.bl.uk', + backend => 'Dummy', + censorship => { + censor_notes_staff => 1, + censor_reply_date => 1, + }, + credentials => { + api_application => $application, + api_keys => { + default => { + api_auth => $defaults->{api_auth}, + api_key => $defaults->{api_key}, + } + }, + }, + default_formats => {}, + digital_recipients => {}, + library_privileges => {}, + limits => { default => { count => 10 } }, + prefixes => {}, + spec_file => undef, + }, + "Basic _load_configuration, with limit" +); + +$params->{configuration}->{branch} = $first_branch; +is_deeply( + Koha::Illrequest::Config::_load_configuration($params), + { + api_url => 'http://apitest.bldss.bl.uk', + backend => 'Dummy', + censorship => { + censor_notes_staff => 1, + censor_reply_date => 1, + }, + credentials => { + api_keys => { + default => { + api_key => $defaults->{api_key}, + api_auth => $defaults->{api_auth}, + }, + $first_branch->{code} => { + api_key => $first_branch->{api_key}, + api_auth => $first_branch->{api_auth}, + }, + }, + api_application => $application, + }, + default_formats => {}, + digital_recipients => {}, + library_privileges => {}, + limits => { default => { count => 10 } }, + prefixes => {}, + spec_file => undef, + }, + "Single Branch _load_configuration" +); + +$params->{configuration}->{branch} = [ $first_branch, $second_branch ]; +is_deeply( + Koha::Illrequest::Config::_load_configuration($params), + { + api_url => 'http://apitest.bldss.bl.uk', + credentials => { + api_keys => { + default => { + api_key => $defaults->{api_key}, + api_auth => $defaults->{api_auth}, + }, + $first_branch->{code} => { + api_key => $first_branch->{api_key}, + api_auth => $first_branch->{api_auth}, + }, + $second_branch->{code} => { + api_key => $second_branch->{api_key}, + api_auth => $second_branch->{api_auth}, + }, + }, + api_application => $application, + }, + backend => 'Dummy', + censorship => { + censor_notes_staff => 1, + censor_reply_date => 1, + }, + default_formats => {}, + digital_recipients => {}, + library_privileges => {}, + limits => { + default => {count => 10 }, + branch => { + $second_branch->{code} => {count => 5 }, + }, + }, + prefixes => {}, + spec_file => undef, + }, + "Multi Branch _load_configuration" +); + +dies_ok { Koha::Illrequest::Config::_load_configuration($params, 1) } + "Unmediated, missing config _load_configuration"; + +$params->{configuration}->{default_formats} = { + format => 1, quality => 1, quantity => 1, service => 1, speed => 1 +}; +is_deeply( + Koha::Illrequest::Config::_load_configuration($params, 1), + { + api_url => 'http://apitest.bldss.bl.uk', + backend => 'Dummy', + censorship => { + censor_notes_staff => 1, + censor_reply_date => 1, + }, + credentials => { + api_keys => { + default => { + api_key => $defaults->{api_key}, + api_auth => $defaults->{api_auth}, + }, + $first_branch->{code} => { + api_key => $first_branch->{api_key}, + api_auth => $first_branch->{api_auth}, + }, + $second_branch->{code} => { + api_key => $second_branch->{api_key}, + api_auth => $second_branch->{api_auth}, + }, + }, + api_application => $application, + }, + default_formats => { + default => { + format => 1, + quality => 1, + quantity => 1, + service => 1, + speed => 1 + } + }, + digital_recipients => {}, + library_privileges => {}, + limits => { + default => {count => 10 }, + branch => { + $second_branch->{code} => {count => 5 }, + }, + }, + prefixes => {}, + spec_file => undef, + }, + "default_formats, default only _load_configuration" +); + +# getDefaultFormats +dies_ok { $config->getLimitRules('wrongType') } + "Faulty getDefaultFormats"; + +$config->{configuration} = + Koha::Illrequest::Config::_load_configuration($params); +is_deeply( + $config->getDefaultFormats('brw_cat'), + { + default => { + format => 1, + quality => 1, + quantity => 1, + service => 1, + speed => 1 + } + }, + "Default getDefaultFormats" +); + +# getLimitRules +dies_ok { $config->getLimitRules('wrongType') } + "Faulty getLimitRules"; + +$config->{configuration} = + Koha::Illrequest::Config::_load_configuration($params); +is_deeply( + $config->getLimitRules('branch'), + { + default => { count => 10 }, + second => { count => 5 }, + }, + "second branch getLimitRules" +); + +is_deeply( + $config->getLimitRules('brw_cat'), + { + default => { count => 10 }, + }, + "empty brw_cat getLimitRules" +); + +# getCredentials +$params = { + application => $application, + backend => 'Dummy', + configuration => {}, + reply_date => "hide", + staff_request_comments => "hide", +}; +$config->{configuration} = + Koha::Illrequest::Config::_load_configuration($params); +is_deeply( + $config->getCredentials, + { + api_key => 0, + api_key_auth => 0, + api_application => $application->{key}, + api_application_auth => $application->{auth}, + }, + "getCredentials, no creds, just App." +); + +$params->{configuration} = $defaults; +$config->{configuration} = + Koha::Illrequest::Config::_load_configuration($params), +is_deeply( + $config->getCredentials, + { + api_key => $defaults->{api_key}, + api_key_auth => $defaults->{api_auth}, + api_application => $application->{key}, + api_application_auth => $application->{auth}, + }, + "getCredentials, default creds & App." +); + +$params->{configuration}->{branch} = $first_branch; +$config->{configuration} = + Koha::Illrequest::Config::_load_configuration($params), +is_deeply( + $config->getCredentials($first_branch->{code}), + { + api_key => $first_branch->{api_key}, + api_key_auth => $first_branch->{api_auth}, + api_application => $application->{key}, + api_application_auth => $application->{auth}, + }, + "getCredentials, $first_branch->{code} creds & App." +); + +is_deeply( + $config->getCredentials("random"), + { + api_key => $defaults->{api_key}, + api_key_auth => $defaults->{api_auth}, + api_application => $application->{key}, + api_application_auth => $application->{auth}, + }, + "getCredentials, fallback creds & app." +); + +done_testing; diff --git a/t/db_dependent/Illrequest/Dummy.t b/t/db_dependent/Illrequest/Dummy.t new file mode 100644 index 0000000..38b5fac --- /dev/null +++ b/t/db_dependent/Illrequest/Dummy.t @@ -0,0 +1,378 @@ +#!/usr/bin/perl +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +use Modern::Perl; + +use Koha::Database; +use Koha::Illrequests; +use t::lib::TestBuilder; + +use Test::More tests => 15; + +# This is a set of basic tests for the Dummy backend, largely to provide +# sanity checks for testing at the higher level Illrequest.pm level. +# +# The Dummy backend is rather simple, but provides correctly formatted +# response values, that other backends can model themselves after. + +use_ok('Koha::Illrequest::Backend::Dummy'); + +my $backend = Koha::Illrequest::Backend::Dummy->new; + +isa_ok($backend, 'Koha::Illrequest::Backend::Dummy'); + + +my $schema = Koha::Database->new->schema; +$schema->storage->txn_begin; + +my $builder = t::lib::TestBuilder->new; + +my $patron = $builder->build({ source => 'Borrower' }); +my $branch = $builder->build({ source => 'Branch' }); + +my $illRequest = $builder->build({ + source => 'Illrequest', + value => { + borrowernumber => $patron->{borrowernumber}, + branch => $branch->{branchcode}, + } +}); +my $mock_request = Koha::Illrequests->find($illRequest->{illrequest_id}); +$mock_request->_config->backend("Dummy"); +$mock_request->_config->limits({ default => { count => -1 } }); + +# Test Create +my $rq = $backend->create({ + request => $mock_request, + method => 'create', + stage => 'search_form', + other => undef, +}); + +is_deeply( + $rq, + { + error => 0, + status => '', + message => '', + method => 'create', + stage => 'search_form', + value => {}, + }, + "Search_Form stage of create method." +); + +$rq = $backend->create({ + request => $mock_request, + method => 'create', + stage => 'search_results', + other => { search => "interlibrary loans" }, +}); + +is_deeply( + $rq, + { + error => 0, + status => '', + message => '', + method => 'create', + stage => 'search_results', + value => [ + { + id => 1234, + title => "Ordering ILLs using Koha", + author => "A.N. Other", + }, + { + id => 5678, + title => "Interlibrary loans in Koha", + author => "A.N. Other", + }, + ], + }, + "Search_Results stage of create method." +); + +$rq = $backend->create({ + request => $mock_request, + method => 'create', + stage => 'commit', + other => { + id => 1234, + title => "Ordering ILLs using Koha", + author => "A.N. Other", + }, +}); + +is_deeply( + $rq, + { + error => 0, + status => '', + message => '', + method => 'create', + stage => 'commit', + value => { + id => 1234, + title => "Ordering ILLs using Koha", + author => "A.N. Other" + }, + }, + "Commit stage of create method." +); + +$rq = $backend->create({ + request => $mock_request, + method => 'create', + stage => 'unknown_stage', + other => { + id => 1234, + title => "Ordering ILLs using Koha", + author => "A.N. Other", + }, +}); + +is_deeply( + $rq, + { + error => 1, + status => 'unknown_stage', + message => '', + method => 'create', + stage => 'unknown_stage', + value => {}, + }, + "Commit stage of create method." +); + +# Test Confirm + +$rq = $backend->confirm({ + request => $mock_request, + other => undef, +}); + +is_deeply( + $rq, + { + error => 0, + status => '', + message => '', + method => 'confirm', + stage => 'commit', + value => { + id => 1234, + title => "Ordering ILLs using Koha", + author => "A.N. Other", + status => "On order", + cost => "30 GBP", + }, + }, + "Basic confirm method." +); + +# Test List + +is_deeply( + $backend->list, + { + error => 0, + status => '', + message => '', + method => 'list', + stage => 'commit', + value => { + 1 => { + id => 1234, + title => "Ordering ILLs using Koha", + author => "A.N. Other", + status => "On order", + cost => "30 GBP", + }, + }, + }, + "Basic list method." +); + +# Test Renew + +is_deeply( + $backend->renew({ + request => $mock_request, + other => undef, + }), + { + error => 1, + status => 'not_renewed', + message => 'Order not yet delivered.', + method => 'renew', + stage => 'commit', + value => { + id => 1234, + title => "Ordering ILLs using Koha", + author => "A.N. Other", + status => "On order", + }, + }, + "Basic renew method." +); + +Koha::Illrequestattributes->find({ + illrequest_id => $mock_request->illrequest_id, + type => "status" +})->set({ value => "Delivered" })->store; + +is_deeply( + $backend->renew({ + request => $mock_request, + other => undef, + }), + { + error => 0, + status => '', + message => '', + method => 'renew', + stage => 'commit', + value => { + id => 1234, + title => "Ordering ILLs using Koha", + author => "A.N. Other", + status => "Renewed", + }, + }, + "Modified renew method." +); + +# Test Update_Status + +is_deeply( + $backend->update_status({ + request => $mock_request, + other => undef, + }), + { + error => 1, + status => 'failed_update_hook', + message => 'Fake reason for failing to perform update operation.', + method => 'update_status', + stage => 'commit', + value => { + id => 1234, + title => "Ordering ILLs using Koha", + author => "A.N. Other", + status => "Delivered", + }, + }, + "Basic update_status method." +); + +# FIXME: Perhaps we should add a test checking for specific status code +# transitions. + +# Test Cancel + +is_deeply( + $backend->cancel({ + request => $mock_request, + other => undef, + }), + { + error => 0, + status => '', + message => '', + method => 'cancel', + stage => 'commit', + value => { + id => 1234, + title => "Ordering ILLs using Koha", + author => "A.N. Other", + status => "Delivered", + }, + }, + "Basic cancel method." +); + +is_deeply( + $backend->cancel({ + request => $mock_request, + other => undef, + }), + { + error => 1, + status => 'unknown_request', + message => 'Cannot cancel an unknown request.', + method => 'cancel', + stage => 'commit', + value => { + id => 1234, + title => "Ordering ILLs using Koha", + author => "A.N. Other", + }, + }, + "Attempt to cancel an unconfirmed request." +); + +# Test Status + +is_deeply( + $backend->status({ + request => $mock_request, + other => undef, + }), + { + error => 1, + status => 'unknown_request', + message => 'Cannot query status of an unknown request.', + method => 'status', + stage => 'commit', + value => { + id => 1234, + title => "Ordering ILLs using Koha", + author => "A.N. Other", + }, + }, + "Attempt to get status of an unconfirmed request." +); + +$rq = $backend->confirm({ + request => $mock_request, + other => undef, +}); + +is_deeply( + $backend->status({ + request => $mock_request, + other => undef, + }), + { + error => 0, + status => '', + message => '', + method => 'status', + stage => 'commit', + value => { + id => 1234, + title => "Ordering ILLs using Koha", + author => "A.N. Other", + status => "On order", + }, + }, + "Basic status method." +); + +1; diff --git a/t/db_dependent/api/v1/illrequests.t b/t/db_dependent/api/v1/illrequests.t new file mode 100644 index 0000000..fb306ba --- /dev/null +++ b/t/db_dependent/api/v1/illrequests.t @@ -0,0 +1,136 @@ +#!/usr/bin/env perl + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Test::More tests => 1; +use Test::Mojo; +use Test::Warn; + +use t::lib::TestBuilder; +use t::lib::Mocks; + +use C4::Auth; +use Koha::Illrequests; + +my $schema = Koha::Database->new->schema; +my $builder = t::lib::TestBuilder->new; + +# FIXME: sessionStorage defaults to mysql, but it seems to break transaction handling +# this affects the other REST api tests +t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' ); + +my $remote_address = '127.0.0.1'; +my $t = Test::Mojo->new('Koha::REST::V1'); + +subtest 'list() tests' => sub { + + plan tests => 6; + + $schema->storage->txn_begin; + + Koha::Illrequests->search->delete; + my ( $borrowernumber, $session_id ) = + create_user_and_session( { authorized => 1 } ); + + ## Authorized user tests + # No requests, so empty array should be returned + my $tx = $t->ua->build_tx( GET => '/api/v1/illrequests' ); + $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); + $tx->req->env( { REMOTE_ADDR => $remote_address } ); + $t->request_ok($tx)->status_is(200)->json_is( [] ); + +# my $city_country = 'France'; +# my $city = $builder->build( +# { source => 'City', value => { city_country => $city_country } } ); +# +# # One city created, should get returned +# $tx = $t->ua->build_tx( GET => '/api/v1/cities' ); +# $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); +# $tx->req->env( { REMOTE_ADDR => $remote_address } ); +# $t->request_ok($tx)->status_is(200)->json_is( [$city] ); +# +# my $another_city = $builder->build( +# { source => 'City', value => { city_country => $city_country } } ); +# my $city_with_another_country = $builder->build( { source => 'City' } ); +# +# # Two cities created, they should both be returned +# $tx = $t->ua->build_tx( GET => '/api/v1/cities' ); +# $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); +# $tx->req->env( { REMOTE_ADDR => $remote_address } ); +# $t->request_ok($tx)->status_is(200) +# ->json_is( [ $city, $another_city, $city_with_another_country ] ); +# +# # Filtering works, two cities sharing city_country +# $tx = +# $t->ua->build_tx( GET => "/api/v1/cities?city_country=" . $city_country ); +# $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); +# $tx->req->env( { REMOTE_ADDR => $remote_address } ); +# my $result = +# $t->request_ok($tx)->status_is(200)->json_is( [ $city, $another_city ] ); +# +# $tx = $t->ua->build_tx( +# GET => "/api/v1/cities?city_name=" . $city->{city_name} ); +# $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); +# $tx->req->env( { REMOTE_ADDR => $remote_address } ); +# $t->request_ok($tx)->status_is(200)->json_is( [$city] ); + + # Warn on unsupported query parameter + $tx = $t->ua->build_tx( GET => '/api/v1/illrequests?request_blah=blah' ); + $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); + $tx->req->env( { REMOTE_ADDR => $remote_address } ); + $t->request_ok($tx)->status_is(400)->json_is( + [{ path => '/query/request_blah', message => 'Malformed query string'}] + ); + + $schema->storage->txn_rollback; +}; + +sub create_user_and_session { + + my $args = shift; + my $flags = ( $args->{authorized} ) ? $args->{authorized} : 0; + my $dbh = C4::Context->dbh; + + my $user = $builder->build( + { + source => 'Borrower', + value => { + flags => $flags + } + } + ); + + # Create a session for the authorized user + my $session = C4::Auth::get_session(''); + $session->param( 'number', $user->{borrowernumber} ); + $session->param( 'id', $user->{userid} ); + $session->param( 'ip', '127.0.0.1' ); + $session->param( 'lasttime', time() ); + $session->flush; + + if ( $args->{authorized} ) { + $dbh->do( " + INSERT INTO user_permissions (borrowernumber,module_bit,code) + VALUES (?,3,'parameters_remaining_permissions')", undef, + $user->{borrowernumber} ); + } + + return ( $user->{borrowernumber}, $session->id ); +} + +1; -- 2.7.4