Koha-clms/suggestion/suggestion.pl

619 lines
24 KiB
Perl
Executable File

#!/usr/bin/perl
# This file is part of Koha.
# Copyright 2006-2010 BibLibre
#
# 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 <https://www.gnu.org/licenses>.
use Modern::Perl;
require Exporter;
use CGI qw ( -utf8 );
use C4::Auth qw( get_template_and_user );
use C4::Output qw( output_html_with_http_headers output_and_exit_if_error );
use C4::Suggestions;
use C4::Koha qw( GetAuthorisedValues );
use C4::Budgets qw( GetBudget GetBudgets GetBudgetHierarchy CanUserUseBudget );
use C4::Search qw( FindDuplicate GetDistinctValues );
use C4::Members;
use Koha::DateUtils qw( dt_from_string );
use Koha::AuthorisedValues;
use Koha::Acquisition::Currencies;
use Koha::Libraries;
use Koha::Patrons;
use Koha::Suggestion;
use Koha::Suggestions;
use Koha::Token;
use URI::Escape qw( uri_escape );
sub GetCriteriumDesc {
my ( $criteriumvalue, $displayby ) = @_;
if ( $displayby =~ /status/i ) {
unless ( grep { /$criteriumvalue/ } qw(ASKED ACCEPTED REJECTED CHECKED ORDERED AVAILABLE) ) {
my $av =
Koha::AuthorisedValues->search( { category => 'SUGGEST_STATUS', authorised_value => $criteriumvalue } );
return $av->count ? $av->next->lib : 'Unknown';
}
return ( $criteriumvalue eq 'ASKED' ? "Pending" : ucfirst( lc($criteriumvalue) ) )
if ( $displayby =~ /status/i );
}
if ( $displayby =~ /branchcode/ ) {
return $criteriumvalue ? Koha::Libraries->find($criteriumvalue)->branchname : "__ANY__";
}
if ( $displayby =~ /itemtype/ ) {
my $av =
Koha::AuthorisedValues->search( { category => 'SUGGEST_FORMAT', authorised_value => $criteriumvalue } );
return $av->count ? $av->next->lib : 'Unknown';
}
if ( $displayby =~ /suggestedby/ || $displayby =~ /managedby/ || $displayby =~ /acceptedby/ ) {
my $patron = Koha::Patrons->find($criteriumvalue);
return "" unless $patron;
return $patron->surname . ", " . $patron->firstname;
}
if ( $displayby =~ /budgetid/ ) {
my $budget = GetBudget($criteriumvalue);
return "" unless $budget;
return $$budget{budget_name};
}
}
my $input = CGI->new;
my $redirect = $input->param('redirect');
my $suggestedbyme = ( defined $input->param('suggestedbyme') ? $input->param('suggestedbyme') : 1 );
my $op = $input->param('op') || 'else';
my @editsuggestions = $input->multi_param('suggestionid');
my $suggestedby = $input->param('suggestedby');
my $returnsuggestedby = $input->param('returnsuggestedby');
my $returnsuggested = $input->param('returnsuggested');
my $managedby = $input->param('managedby');
my $displayby = $input->param('displayby') || '';
my $tabcode = $input->param('tabcode');
my $save_confirmed = $input->param('save_confirmed') || 0;
my $notify = $input->param('notify');
my $filter_archived = $input->param('filter_archived') || 0;
my $new_itemtype = $input->param('suggestion_itemtype');
my $sugg_managedby = $input->param('suggestion_managedby');
#NOTE: Validate displayby as it gets passed to sensitive functions
my %valid_displayby = (
STATUS => 1,
branchcode => 1,
itemtype => 1,
managedby => 1,
acceptedby => 1,
);
if ( !$valid_displayby{$displayby} ) {
$displayby = '';
}
my $reasonsloop = GetAuthorisedValues("SUGGEST");
my $suggestion_ref = { $input->Vars };
my $stored_suggestion;
if ( $op eq "edit_form" || $op eq "show" ) {
$stored_suggestion = Koha::Suggestions->find( $input->param('suggestionid') ) if $input->param('suggestionid');
}
delete $suggestion_ref->{$_} for qw(csrf_token suggestion_itemtype suggestion_managedby table_1_length);
# get only the columns of Suggestion
my $schema = Koha::Database->new()->schema;
my $columns = ' ' . join( ' ', $schema->source('Suggestion')->columns ) . ' ';
my $suggestion_only = { map { $columns =~ / $_ / ? ( $_ => $suggestion_ref->{$_} ) : () } keys %$suggestion_ref };
$suggestion_only->{STATUS} = $suggestion_ref->{STATUS};
delete $$suggestion_ref{$_}
foreach
qw( suggestedbyme displayby tabcode notify filter_archived koha_login_context auth_forwarded_hash login_op login_password login_userid );
foreach my $key ( keys %$suggestion_ref ) {
delete $suggestion_ref->{$key} if ( !$suggestion_ref->{$key} && ( $op eq 'else' ) );
delete $suggestion_ref->{$key} if $key =~ m{^DataTables_acqui_suggestions_suggestions};
}
delete $suggestion_only->{branchcode} if $suggestion_only->{branchcode} eq '__ANY__';
delete $suggestion_only->{budgetid} if $suggestion_only->{budgetid} eq '__ANY__';
unless ( $op eq 'cud-save' ) {
while ( my ( $k, $v ) = each %$suggestion_only ) {
delete $suggestion_only->{$k} if $v eq '';
}
}
my ( $template, $borrowernumber, $cookie, $userflags ) = get_template_and_user(
{
template_name => "suggestion/suggestion.tt",
query => $input,
type => "intranet",
flagsrequired => { suggestions => '*' },
}
);
my $librarian = Koha::Patrons->find($borrowernumber);
$borrowernumber = $input->param('borrowernumber') if ( $input->param('borrowernumber') );
$template->param( 'borrowernumber' => $borrowernumber );
my $branchfilter = $input->param('branchcode') || C4::Context->userenv->{'branch'};
#########################################
## Operations
##
my @messages;
if ( $op =~ /cud-save/ ) {
output_and_exit_if_error( $input, $cookie, $template, { check => 'csrf_token' } );
my @messages;
my $biblio = MarcRecordFromNewSuggestion(
{
title => $suggestion_only->{title},
author => $suggestion_only->{author},
itemtype => $suggestion_only->{itemtype},
isbn => $suggestion_only->{isbn},
}
);
my $manager = Koha::Patrons->find( $suggestion_only->{managedby} );
if ( $manager && not $manager->has_permission( { suggestions => 'suggestions_manage' } ) ) {
push @messages, { type => 'error', code => 'manager_not_enough_permissions' };
$template->param(
messages => \@messages,
);
} elsif ( !$suggestion_only->{suggestionid}
&& ( my ( $duplicatebiblionumber, $duplicatetitle ) = FindDuplicate($biblio) )
&& !$save_confirmed )
{
push @messages,
{ type => 'error', code => 'biblio_exists', id => $duplicatebiblionumber, title => $duplicatetitle };
$stored_suggestion = Koha::Suggestion->new($suggestion_only);
$template->param(
messages => \@messages,
need_confirm => 1
);
$op = 'save';
} else {
for my $date_key (qw( suggesteddate manageddate accepteddate rejecteddate )) {
# FIXME Do we need this?
$suggestion_only->{$date_key} = dt_from_string( $suggestion_only->{$date_key} )
if $suggestion_only->{$date_key};
}
if ( $suggestion_only->{"STATUS"} ) {
if ( my $tmpstatus = lc( $suggestion_only->{"STATUS"} ) =~ /ACCEPTED|REJECTED/i ) {
$suggestion_only->{ lc( $suggestion_only->{"STATUS"} ) . "date" } = dt_from_string;
$suggestion_only->{ lc( $suggestion_only->{"STATUS"} ) . "by" } = C4::Context->userenv->{number};
}
$suggestion_only->{manageddate} = dt_from_string;
$suggestion_only->{"managedby"} ||= C4::Context->userenv->{number};
}
my $otherreason = $input->param('other_reason');
if ( $suggestion_only->{reason} eq 'other' && $otherreason ) {
$suggestion_only->{reason} = $otherreason;
}
if ( $suggestion_only->{'suggestionid'} > 0 ) {
$suggestion_only->{lastmodificationdate} = dt_from_string;
$suggestion_only->{lastmodificationby} = C4::Context->userenv->{number};
$suggestion_only->{branchcode} = undef
if exists $suggestion_only->{branchcode}
&& $suggestion_only->{branchcode} eq "";
if ( $librarian->has_permission( { 'suggestions' => 'suggestions_manage' } ) ) {
&ModSuggestion($suggestion_only);
} else {
push @messages, { type => 'error', code => 'no_manage_permission' };
$template->param( messages => \@messages, );
}
if ($notify) {
my $patron = Koha::Patrons->find( $suggestion_only->{managedby} );
my $email_address = $patron->notice_email_address;
if ( $patron->notice_email_address ) {
my $letter = C4::Letters::GetPreparedLetter(
module => 'suggestions',
letter_code => 'NOTIFY_MANAGER',
branchcode => $patron->branchcode,
lang => $patron->lang,
tables => {
suggestions => $suggestion_only->{suggestionid},
branches => $patron->branchcode,
borrowers => $patron->borrowernumber,
},
);
C4::Letters::EnqueueLetter(
{
letter => $letter,
borrowernumber => $patron->borrowernumber,
message_transport_type => 'email'
}
);
}
}
} else {
###FIXME:Search here if suggestion already exists.
my $suggestions = Koha::Suggestions->search_limited($suggestion_only);
if ( $suggestions->count ) {
#some suggestion are answering the request, do not add
my @messages;
while ( my $suggestion = $suggestions->next ) {
push @messages, { type => 'error', code => 'already_exists', id => $suggestion->suggestionid };
}
$template->param( messages => \@messages );
} else {
## Adding some information related to suggestion
if ( $librarian->has_permission( { 'suggestions' => 'suggestions_create' } ) ) {
Koha::Suggestion->new($suggestion_only)->store();
} else {
push @messages, { type => 'error', code => 'no_delete_permission' };
$template->param( messages => \@messages );
}
}
# empty fields, to avoid filter in "SearchSuggestion"
}
map { delete $$suggestion_ref{$_} unless $_ eq 'branchcode' } keys %$suggestion_ref;
$op = 'else';
if ( $redirect eq 'purchase_suggestions' ) {
print $input->redirect("/cgi-bin/koha/members/purchase-suggestions.pl?borrowernumber=$borrowernumber");
exit;
}
}
} elsif ( $op eq 'add_form' ) {
# get the patron that is suggesting or fall back to the logged-in-user
my $default_suggester = $suggestedby ? Koha::Patrons->find($suggestedby) : $librarian;
$template->param(
default_suggester => $default_suggester,
default_suggesteddate => dt_from_string,
default_branch => C4::Context->userenv->{"branch"},
);
$op = 'save';
} elsif ( $op eq 'edit_form' ) {
#Edit suggestion
my $other_reason = 1;
foreach my $reason ( @{$reasonsloop} ) {
if ( $stored_suggestion->reason eq $reason->{lib} ) {
$other_reason = 0;
}
}
$other_reason = 0 unless $stored_suggestion->reason;
$template->param(
other_reason => $other_reason,
default_manageddate => dt_from_string,
);
$op = 'save';
} elsif ( $op eq "cud-update_status" ) {
my $suggestion;
# set accepted/rejected/managed information if applicable
# ie= if the librarian has chosen some action on the suggestions
my $STATUS = $input->param('STATUS');
my $accepted_by = $input->param('acceptedby');
if ( $STATUS eq "ACCEPTED" ) {
$suggestion = {
accepteddate => dt_from_string,
acceptedby => C4::Context->userenv->{number},
};
} elsif ( $STATUS eq "REJECTED" ) {
$suggestion = {
rejecteddate => dt_from_string,
rejectedby => C4::Context->userenv->{number},
};
}
if ($STATUS) {
$suggestion->{manageddate} = dt_from_string;
$suggestion->{managedby} = C4::Context->userenv->{number};
$suggestion->{STATUS} = $STATUS;
}
if ( my $reason = $input->param("reason") ) {
if ( $reason eq "other" ) {
$reason = $input->param("other_reason");
}
$suggestion->{reason} = $reason;
}
if ( $librarian->has_permission( { 'suggestions' => 'suggestions_manage' } ) ) {
foreach my $suggestionid (@editsuggestions) {
next unless $suggestionid;
$suggestion->{suggestionid} = $suggestionid;
&ModSuggestion($suggestion);
}
redirect_with_params($input);
} else {
push @messages, { type => 'error', code => 'no_manage_permission' };
$template->param( messages => \@messages, );
}
redirect_with_params($input);
} elsif ( $op eq "cud-delete" ) {
if ( $librarian->has_permission( { 'suggestions' => 'suggestions_delete' } ) ) {
foreach my $delete_field (@editsuggestions) {
&DelSuggestion( $borrowernumber, $delete_field, 'intranet' );
}
redirect_with_params($input);
} else {
push @messages, { type => 'error', code => 'no_delete_permission' };
$template->param( messages => \@messages, );
}
} elsif ( $op eq "cud-archive" ) {
Koha::Suggestions->find($_)->update( { archived => 1 } ) for @editsuggestions;
redirect_with_params($input);
} elsif ( $op eq "cud-unarchive" ) {
Koha::Suggestions->find($_)->update( { archived => 0 } ) for @editsuggestions;
redirect_with_params($input);
} elsif ( $op eq 'cud-update_itemtype' ) {
if ( $librarian->has_permission( { 'suggestions' => 'suggestions_manage' } ) ) {
foreach my $suggestionid (@editsuggestions) {
next unless $suggestionid;
&ModSuggestion( { suggestionid => $suggestionid, itemtype => $new_itemtype } );
}
redirect_with_params($input);
} else {
push @messages, { type => 'error', code => 'no_manage_permission' };
$template->param( messages => \@messages, );
}
} elsif ( $op eq 'cud-update_manager' ) {
if ( $librarian->has_permission( { 'suggestions' => 'suggestions_manage' } ) ) {
foreach my $suggestionid (@editsuggestions) {
next unless $suggestionid;
&ModSuggestion( { suggestionid => $suggestionid, managedby => $sugg_managedby } );
}
redirect_with_params($input);
} else {
push @messages, { type => 'error', code => 'no_manage_permission' };
$template->param( messages => \@messages, );
}
}
if ( $op eq 'else' ) {
$displayby ||= "STATUS";
# distinct values of display by
my $criteria_list = GetDistinctValues( "suggestions." . $displayby );
my ( @criteria_dv, $criteria_has_empty );
foreach (@$criteria_list) {
if ( $_->{value} ) {
push @criteria_dv, $_->{value};
} else {
$criteria_has_empty = 1;
}
}
# aggregate null and empty values under empty value
push @criteria_dv, '' if $criteria_has_empty;
# Hack to not modify GetDistinctValues for this specific case
if ( $displayby eq 'branchcode'
&& C4::Context->preference('IndependentBranches')
&& not C4::Context->IsSuperLibrarian )
{
@criteria_dv = ( C4::Context->userenv->{'branch'} );
}
# Pending tab first
if ( $displayby eq 'STATUS' ) {
@criteria_dv = grep { $_ ne 'ASKED' } @criteria_dv;
unshift @criteria_dv, 'ASKED';
}
unless ( exists $suggestion_ref->{branchcode} ) {
$suggestion_ref->{branchcode} = C4::Context->userenv->{'branch'};
}
my $search_params = {%$suggestion_ref};
# filter on date fields
foreach my $field (qw( suggesteddate manageddate accepteddate )) {
my $from = delete $search_params->{"${field}_from"};
my $to = delete $search_params->{"${field}_to"};
my $from_dt = $from && eval { dt_from_string($from) };
my $to_dt = $to && eval { dt_from_string($to) };
if ( $from_dt || $to_dt ) {
my $dtf = Koha::Database->new->schema->storage->datetime_parser;
if ( $from_dt && $to_dt ) {
$search_params->{$field} =
{ -between => [ $dtf->format_date($from_dt), $dtf->format_date($to_dt) ] };
} elsif ($from_dt) {
$search_params->{$field} = { '>=' => $dtf->format_date($from_dt) };
} elsif ($to_dt) {
$search_params->{$field} = { '<=' => $dtf->format_date($to_dt) };
}
}
}
if ( $search_params->{budgetid} && $search_params->{budgetid} eq '__NONE__' ) {
$search_params->{budgetid} = [ undef, '' ];
}
for my $f (qw (branchcode budgetid)) {
delete $search_params->{$f}
if $search_params->{$f} eq '__ANY__'
|| $search_params->{$f} eq '';
}
for my $bi (qw (title author isbn publishercode copyrightdate collectiontitle)) {
if ( $search_params->{$bi} ) {
$search_params->{"me.$bi"} = { 'like' => "%" . $search_params->{$bi} . "%" };
delete $search_params->{$bi};
}
}
$search_params->{archived} = 0 if !$filter_archived;
foreach my $key ( keys %$search_params ) {
if ( $key eq 'branchcode' ) {
if ( $displayby ne 'branchcode' ) {
my $branch_param = delete $search_params->{$key};
$search_params->{"me.branchcode"} = $branch_param;
} else {
delete $search_params->{$key};
}
}
}
# Retrieve the count to display on each tab
my $suggestion_tabs = Koha::Suggestions->search_limited(
$search_params,
{
select => [ $displayby, { count => $displayby, -as => 'count' } ],
as => [ $displayby, 'count' ],
group_by => [$displayby],
}
);
my $tab_counts = { map { $_->get_column($displayby) => $_->get_column('count') } $suggestion_tabs->as_list };
my @allsuggestions;
foreach my $criteriumvalue (@criteria_dv) {
# By default, display suggestions from current working branch
my $definedvalue = defined $$suggestion_ref{$displayby} && $$suggestion_ref{$displayby} ne "";
next
if $search_params->{STATUS}
&& $displayby eq 'STATUS'
&& $criteriumvalue ne $search_params->{STATUS};
next
if ( $definedvalue && $$suggestion_ref{$displayby} ne $criteriumvalue )
and ( $displayby ne 'branchcode' && $branchfilter ne '__ANY__' );
my $criterium_search_params = {%$search_params};
$criterium_search_params->{"me.$displayby"} = $criteriumvalue;
push @allsuggestions,
{
"suggestiontype" => $criteriumvalue || "suggest",
"suggestiontypelabel" => GetCriteriumDesc( $criteriumvalue, $displayby ) || "",
'search_params' => $criterium_search_params,
'tab_count' => $tab_counts->{$criteriumvalue},
}
if $tab_counts->{$criteriumvalue} > 0;
delete $$suggestion_ref{$displayby} unless $definedvalue;
}
$template->param(
"displayby" => $displayby,
"notabs" => $displayby eq "",
suggestions => \@allsuggestions,
);
}
$template->param(
suggestion => $stored_suggestion,
filter_archived => $filter_archived,
"op" => $op,
reasonsloop => $reasonsloop,
redirect => $redirect,
);
if ( defined($returnsuggested) and $returnsuggested ne "no_one" ) {
print $input->redirect( "/cgi-bin/koha/members/moremember.pl?borrowernumber=" . $returnsuggested . "#suggestions" );
}
$template->param(
branchfilter => $branchfilter,
);
$template->param( returnsuggestedby => $returnsuggestedby );
my $patron_reason_loop = GetAuthorisedValues("OPAC_SUG");
$template->param( patron_reason_loop => $patron_reason_loop );
# Budgets for suggestion add or edition
my $sugg_budget_loop = [];
my $sugg_budgets = GetBudgetHierarchy();
foreach my $r ( @{$sugg_budgets} ) {
next unless ( CanUserUseBudget( $librarian->unblessed, $r, $userflags ) );
my $selected = ( $$suggestion_ref{budgetid} && $r->{budget_id} eq $$suggestion_ref{budgetid} ) ? 1 : 0;
push @{$sugg_budget_loop},
{
b_id => $r->{budget_id},
b_txt => $r->{budget_name},
b_active => $r->{budget_period_active},
selected => $selected,
};
}
@{$sugg_budget_loop} = sort { uc( $a->{b_txt} ) cmp uc( $b->{b_txt} ) } @{$sugg_budget_loop};
$template->param( sugg_budgets => $sugg_budget_loop );
if ( $stored_suggestion && $stored_suggestion->STATUS ) {
$template->param(
"statusselected_" . $stored_suggestion->STATUS => 1,
selected_status => $stored_suggestion->STATUS,
# We need template var selected_status in the second part of the template where template var suggestion.STATUS is out of scope
);
}
my $currencies = Koha::Acquisition::Currencies->search;
$template->param(
currencies => $currencies,
suggestion => $stored_suggestion,
price => sprintf( "%.2f", $$suggestion_ref{'price'} || 0 ),
total => sprintf( "%.2f", $$suggestion_ref{'total'} || 0 ),
);
# lists of distinct values (without empty) for filters
my %hashlists;
foreach my $field (qw(managedby acceptedby suggestedby budgetid)) {
my $values_list;
$values_list = GetDistinctValues( "suggestions." . $field );
my @codes_list = map {
{
'code' => $$_{'value'},
'desc' => GetCriteriumDesc( $$_{'value'}, $field ) || $$_{'value'},
'selected' => ( $$suggestion_ref{$field} ) ? $$_{'value'} eq $$suggestion_ref{$field} : 0,
}
} grep { $$_{'value'} } @$values_list;
@codes_list = sort { $a->{desc} cmp $b->{desc} } @codes_list;
$hashlists{ lc($field) . "_loop" } = \@codes_list;
}
$template->param(
%hashlists,
borrowernumber => ( $input->param('borrowernumber') // undef ),
SuggestionStatuses => GetAuthorisedValues('SUGGEST_STATUS'),
);
output_html_with_http_headers $input, $cookie, $template->output;
sub redirect_with_params {
my ($input) = @_;
my $params = '';
foreach my $key (
qw(
displayby branchcode title author isbn publishercode copyrightdate
collectiontitle suggestedby suggesteddate_from suggesteddate_to
manageddate_from manageddate_to accepteddate_from
accepteddate_to budgetid filter_archived
)
)
{
$params .= $key . '=' . uri_escape( scalar $input->param($key) ) . '&'
if defined( $input->param($key) );
}
print $input->redirect("/cgi-bin/koha/suggestion/suggestion.pl?$params");
exit;
}