Koha/C4/Utils/DataTables/Members.pm
Josef Moravec 2cc3d05d26 Bug 15400: Display date of birth and age more consistantly
Test plan:
0) Apply the patch
1) Go to all of these pages
    Patron detail
    Other patron pages - look on the left side (circ-menu)
    Patron search
    Guarantor search ( go to child patron -> edit -> in guarantor
        section click "Set to patron"
    Search through "Check out" (in the header)

2) Confirm that does show date of birth and date consistantly,
    try it on patrons with and without date of birth set to find
    possible reggressions

Signed-off-by: Aleisha Amohia <aleishaamohia@hotmail.com>

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>

Signed-off-by: Séverine QUEUNE <severine.queune@bulac.fr>

Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de>

Signed-off-by: Michal Denar <black23@gmail.com>

Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
2020-06-29 12:37:02 +02:00

308 lines
11 KiB
Perl

package C4::Utils::DataTables::Members;
use Modern::Perl;
use C4::Context;
use C4::Utils::DataTables;
use Koha::DateUtils;
sub search {
my ( $params ) = @_;
my $searchmember = $params->{searchmember};
my $firstletter = $params->{firstletter};
my $categorycode = $params->{categorycode};
my $branchcode = $params->{branchcode};
my $searchtype = $params->{searchtype} || 'contain';
my $searchfieldstype = $params->{searchfieldstype} || 'standard';
my $has_permission = $params->{has_permission};
my $dt_params = $params->{dt_params};
unless ( $searchmember ) {
$searchmember = $dt_params->{sSearch} // '';
}
# If branches are independent and user is not superlibrarian
# The search has to be only on the user branch
my $userenv = C4::Context->userenv;
my $logged_in_user = Koha::Patrons->find( $userenv->{number} );
my @restricted_branchcodes = $logged_in_user->libraries_where_can_see_patrons;
my ($sth, $query, $iTotalQuery, $iTotalRecords, $iTotalDisplayRecords);
my $dbh = C4::Context->dbh;
# Get the module_bit from a given permission code
if ( $has_permission ) {
($has_permission->{module_bit}) = $dbh->selectrow_array(q|
SELECT bit FROM userflags WHERE flag=?
|, undef, $has_permission->{permission});
}
my (@where, @conditions);
# Get the iTotalRecords DataTable variable
$iTotalQuery = "SELECT COUNT(borrowers.borrowernumber) FROM borrowers";
if ( $has_permission ) {
$iTotalQuery .= ' LEFT JOIN user_permissions ON borrowers.borrowernumber=user_permissions.borrowernumber';
$iTotalQuery .= ' AND module_bit=? AND code=?';
push @conditions, $has_permission->{module_bit}, $has_permission->{subpermission};
}
if ( @restricted_branchcodes ) {
push @where, "borrowers.branchcode IN (" . join( ',', ('?') x @restricted_branchcodes ) . ")";
push @conditions, @restricted_branchcodes;
}
if ( $has_permission ) {
push @where, '( borrowers.flags = 1 OR borrowers.flags & (1 << ?) OR module_bit=? AND code=? )';
push @conditions, ($has_permission->{module_bit}) x 2, $has_permission->{subpermission};
}
$iTotalQuery .= ' WHERE ' . join ' AND ', @where if @where;
($iTotalRecords) = $dbh->selectrow_array( $iTotalQuery, undef, @conditions );
# Do that after iTotalQuery!
if ( defined $branchcode and $branchcode ) {
@restricted_branchcodes = @restricted_branchcodes
? grep ({ $_ eq $branchcode } @restricted_branchcodes)
? ($branchcode)
: (undef) # Do not return any results
: ($branchcode);
}
if ( $searchfieldstype eq 'dateofbirth' ) {
# Return an empty list if the date of birth is not correctly formatted
$searchmember = eval { output_pref( { str => $searchmember, dateformat => 'iso', dateonly => 1 } ); };
if ( $@ or not $searchmember ) {
return {
iTotalRecords => $iTotalRecords,
iTotalDisplayRecords => 0,
patrons => [],
};
}
}
my $select = "SELECT
borrowers.borrowernumber, borrowers.surname, borrowers.firstname,
borrowers.othernames,
borrowers.flags,
borrowers.streetnumber, borrowers.streettype, borrowers.address,
borrowers.address2, borrowers.city, borrowers.state, borrowers.zipcode,
borrowers.country, cardnumber, borrowers.dateexpiry,
borrowers.borrowernotes, borrowers.branchcode, borrowers.email,
borrowers.userid, borrowers.dateofbirth, borrowers.categorycode,
categories.description AS category_description, categories.category_type,
branches.branchname, borrowers.phone";
my $from = "FROM borrowers
LEFT JOIN branches ON borrowers.branchcode = branches.branchcode
LEFT JOIN categories ON borrowers.categorycode = categories.categorycode";
my @where_args;
if ( $has_permission ) {
$from .= '
LEFT JOIN user_permissions ON borrowers.borrowernumber=user_permissions.borrowernumber
AND module_bit=? AND code=?';
push @where_args, $has_permission->{module_bit}, $has_permission->{subpermission};
}
my @where_strs;
if(defined $firstletter and $firstletter ne '') {
push @where_strs, "borrowers.surname LIKE ?";
push @where_args, "$firstletter%";
}
if(defined $categorycode and $categorycode ne '') {
push @where_strs, "borrowers.categorycode = ?";
push @where_args, $categorycode;
}
if(@restricted_branchcodes ) {
push @where_strs, "borrowers.branchcode IN (" . join( ',', ('?') x @restricted_branchcodes ) . ")";
push @where_args, @restricted_branchcodes;
}
my $searchfields = {
standard => C4::Context->preference('DefaultPatronSearchFields') || 'surname,firstname,othernames,cardnumber,userid',
email => 'email,emailpro,B_email',
borrowernumber => 'borrowernumber',
phone => 'phone,phonepro,B_phone,altcontactphone,mobile',
address => 'streetnumber,streettype,address,address2,city,state,zipcode,country',
};
# * is replaced with % for sql
$searchmember =~ s/\*/%/g;
# split into search terms
my @terms;
# consider coma as space
$searchmember =~ s/,/ /g;
if ( $searchtype eq 'contain' ) {
@terms = split / /, $searchmember;
} else {
@terms = ($searchmember);
}
foreach my $term (@terms) {
next unless $term;
my $term_dt = eval { local $SIG{__WARN__} = {}; output_pref( { str => $term, dateonly => 1, dateformat => 'sql' } ); };
if ($term_dt) {
$term = $term_dt;
} else {
$term .= '%' # end with anything
if $term !~ /%$/;
$term = "%$term" # begin with anythin unless start_with
if $searchtype eq 'contain' && $term !~ /^%/;
}
my @where_strs_or;
if ( defined $searchfields->{$searchfieldstype} ) {
for my $searchfield ( split /,/, $searchfields->{$searchfieldstype} ) {
push @where_strs_or, "borrowers." . $dbh->quote_identifier($searchfield) . " LIKE ?";
push @where_args, $term;
}
} else {
push @where_strs_or, "borrowers." . $dbh->quote_identifier($searchfieldstype) . " LIKE ?";
push @where_args, $term;
}
if ( $searchfieldstype eq 'standard' and C4::Context->preference('ExtendedPatronAttributes') and $searchmember ) {
my @matching_borrowernumbers = Koha::Patrons->filter_by_attribute_value($searchmember)->get_column('borrowernumber');
for my $borrowernumber ( @matching_borrowernumbers ) {
push @where_strs_or, "borrowers.borrowernumber = ?";
push @where_args, $borrowernumber;
}
}
push @where_strs, '('. join (' OR ', @where_strs_or) . ')'
if @where_strs_or;
}
if ( $has_permission ) {
push @where_strs, '( borrowers.flags = 1 OR borrowers.flags & (1 << ?) OR module_bit=? AND code=? )';
push @where_args, ($has_permission->{module_bit}) x 2, $has_permission->{subpermission};
}
my $where = @where_strs ? " WHERE " . join (" AND ", @where_strs) : undef;
my $orderby = dt_build_orderby($dt_params);
my $limit;
# If iDisplayLength == -1, we want to display all patrons
if ( !$dt_params->{iDisplayLength} || $dt_params->{iDisplayLength} > -1 ) {
# In order to avoid sql injection
$dt_params->{iDisplayStart} =~ s/\D//g if defined($dt_params->{iDisplayStart});
$dt_params->{iDisplayLength} =~ s/\D//g if defined($dt_params->{iDisplayLength});
$dt_params->{iDisplayStart} //= 0;
$dt_params->{iDisplayLength} //= 20;
$limit = "LIMIT $dt_params->{iDisplayStart},$dt_params->{iDisplayLength}";
}
$query = join(
" ",
($select ? $select : ""),
($from ? $from : ""),
($where ? $where : ""),
($orderby ? $orderby : ""),
($limit ? $limit : "")
);
$sth = $dbh->prepare($query);
$sth->execute(@where_args);
my $patrons = $sth->fetchall_arrayref({});
# Get the iTotalDisplayRecords DataTable variable
$query = "SELECT COUNT(borrowers.borrowernumber) " . $from . ($where ? $where : "");
$sth = $dbh->prepare($query);
$sth->execute(@where_args);
($iTotalDisplayRecords) = $sth->fetchrow_array;
# Get some information on patrons
foreach my $patron (@$patrons) {
my $patron_object = Koha::Patrons->find( $patron->{borrowernumber} );
$patron->{overdues} = $patron_object->get_overdues->count;
$patron->{issues} = $patron_object->checkouts->count;
$patron->{age} = $patron_object->get_age;
my $balance = $patron_object->account->balance;
# FIXME Should be formatted from the template
$patron->{fines} = sprintf("%.2f", $balance);
if( $patron->{dateexpiry} ) {
# FIXME We should not format the date here, do it in template-side instead
$patron->{dateexpiry} = output_pref( { dt => scalar dt_from_string( $patron->{dateexpiry}, 'iso'), dateonly => 1} );
} else {
$patron->{dateexpiry} = '';
}
}
return {
iTotalRecords => $iTotalRecords,
iTotalDisplayRecords => $iTotalDisplayRecords,
patrons => $patrons
}
}
1;
__END__
=head1 NAME
C4::Utils::DataTables::Members - module for using DataTables with patrons
=head1 SYNOPSIS
This module provides (one for the moment) routines used by the patrons search
=head2 FUNCTIONS
=head3 search
my $dt_infos = C4::Utils::DataTables::Members->search($params);
$params is a hashref with some keys:
=over 4
=item searchmember
String to search in the borrowers sql table
=item firstletter
Introduced to contain 1 letter but can contain more.
The search will done on the borrowers.surname field
=item categorycode
Search patrons with this categorycode
=item branchcode
Search patrons with this branchcode
=item searchtype
Can be 'start_with' or 'contain' (default value). Used for the searchmember parameter.
=item searchfieldstype
Can be 'standard' (default value), 'email', 'borrowernumber', 'phone', 'address' or 'dateofbirth', 'sort1', 'sort2'
=item dt_params
Is the reference of C4::Utils::DataTables::dt_get_params($input);
=cut
=back
=head1 LICENSE
This file is part of Koha.
Copyright 2013 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 <http://www.gnu.org/licenses>.