24d6d83ada
This patch add DataTables using server-side processing for the patrons search. It adds: - 1 module C4/Utils/DataTables/Members.pm - 2 services svc/members/search and svc/members/add_to_list - 1 template members/tables/members_results.tt - 1 new practice which is to add template for DataTables in a subdirectory named 'tables'. Impacted scripts: members/members-home.pl and members/members.pl To go further: We can imagine that all patrons searches use the same service with no big changes: 1 little template creates a JSON file and to implement DataTables on the template page, that's all. Amended patch: Since bug 10565 has been pushed, these patches don't apply cleanly. I had to rewrite a part of the patron list feature. I removed the choice to add all resultant patrons from a search. I think this choice is useless with this patch: we are able to display the number of patrons we want and to select all of them. Test plan: - Check that there is no regression on searching patrons. - Try filters on the left of the screen. - Try to sort each column. - Try the "Browse by last name" links. - Check that the "Clear" button clears yours filters. - Try with IndependantBranches ON and OFF. - Verify this feature does not break the patron list feature (cf bug 10565). Signed-off-by: Cedric Vita <cedric.vita@dracenie.com> Signed-off-by: Katrin Fischer <Katrin.Fischer.83@web.de> Passes all tests and QA script, couldn't find any regressions or problems. Some notes left on the bug. Bug 9811: Add unit tests for C4::Utils::DT::Members Signed-off-by: Katrin Fischer <Katrin.Fischer.83@web.de> Bug 9811: QA followup - removes 2 tabs - removes mysqlisms - add sort on borrowernotes - fix wrong capitalization - cat => Category Signed-off-by: Katrin Fischer <Katrin.Fischer.83@web.de> Thx for fixing these! Bug 9811 - multilines notes brakes JSON In new patron search feature, the search results are fetched using Ajax and returned in JSON format. The JSON is created by TT using koha-tmpl/intranet-tmpl/prog/en/modules/members/tables/members_results.tt. One of the fields is the borrower notes. When this notes contains several lines, the JSON is broken. This patch uses TT fileters to consert in notes linefeeds into HTML line break (html_line_break) and then remove linefeeds (collapse). Test plan : - perform a member search that does not return a borrower with a circ note - edit one of the borrowers returned by this search - enter serveral lines of text in "Circulation note" and save - reperform the member search => circ note is well displayed on several lines Bug 9811: use count(primary_key) instead of count(*) Bug 9811: A limit clause should be always added. By default, we want to retrieve 20 first results. Bug 9811: Load the page without any data. Displaying the first 20 patrons is not useful. With this patch, the table is hidden and no record is retrieved by default. On the same way, the existing side effect on redirect disappears. Signed-off-by: Olli-Antti Kivilahti <olli-antti.kivilahti@jns.fi> ------------- -TEST REPORT- ------------- For the filter: Tested all the search fields, branches, search type. Found a bug with "date of birth", followup provided. Tested display limits and verified that AJAX-queries are efficient (using LIMIT clause) to not stress DB needlessly. Tested adding Patrons to a list. A good feature, which seems to work quite well. Signed-off-by: Katrin Fischer <Katrin.Fischer.83@web.de> Adding my test plan to the last patch of this bug. Signed-off-by: Tomas Cohen Arazi <tomascohen@gmail.com>
213 lines
6.8 KiB
Perl
213 lines
6.8 KiB
Perl
package C4::Utils::DataTables::Members;
|
|
|
|
use C4::Branch qw/onlymine/;
|
|
use C4::Context;
|
|
use C4::Members qw/GetMemberIssuesAndFines/;
|
|
use C4::Utils::DataTables;
|
|
use Modern::Perl;
|
|
|
|
sub search {
|
|
my ( $params ) = @_;
|
|
my $searchmember = $params->{searchmember};
|
|
my $firstletter = $params->{firstletter};
|
|
my $categorycode = $params->{categorycode};
|
|
my $branchcode = $params->{branchcode};
|
|
my $searchtype = $params->{searchtype};
|
|
my $searchfieldstype = $params->{searchfieldstype};
|
|
my $dt_params = $params->{dt_params};
|
|
|
|
my ($iTotalRecords, $iTotalDisplayRecords);
|
|
|
|
# If branches are independant and user is not superlibrarian
|
|
# The search has to be only on the user branch
|
|
if ( C4::Branch::onlymine ) {
|
|
my $userenv = C4::Context->userenv;
|
|
$branchcode = $userenv->{'branch'};
|
|
|
|
}
|
|
|
|
my $dbh = C4::Context->dbh;
|
|
my $select = "SELECT
|
|
borrowers.borrowernumber, borrowers.surname, borrowers.firstname, borrowers.address,
|
|
borrowers.address2, borrowers.city, borrowers.zipcode, borrowers.country,
|
|
CAST(borrowers.cardnumber AS UNSIGNED) AS cardnumber, borrowers.dateexpiry,
|
|
borrowers.borrowernotes, borrowers.branchcode,
|
|
categories.description AS category_description, categories.category_type,
|
|
branches.branchname";
|
|
my $from = "FROM borrowers
|
|
LEFT JOIN branches ON borrowers.branchcode = branches.branchcode
|
|
LEFT JOIN categories ON borrowers.categorycode = categories.categorycode";
|
|
my @where_args;
|
|
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(defined $branchcode and $branchcode ne '') {
|
|
push @where_strs, "borrowers.branchcode = ?";
|
|
push @where_args, $branchcode;
|
|
}
|
|
|
|
# split on coma
|
|
$searchmember =~ s/,/ /g if $searchmember;
|
|
my @where_strs_or;
|
|
my $searchfields = {
|
|
standard => 'surname,firstname,othernames,cardnumber',
|
|
email => 'email,emailpro,B_email',
|
|
borrowernumber => 'borrowernumber',
|
|
phone => 'phone,phonepro,B_phone,altcontactphone,mobile',
|
|
address => 'streettype,address,address2,city,state,zipcode,country',
|
|
dateofbirth => 'dateofbirth',
|
|
sort1 => 'sort1',
|
|
sort2 => 'sort2',
|
|
};
|
|
for my $searchfield ( split /,/, $searchfields->{$searchfieldstype} ) {
|
|
foreach my $term ( split / /, $searchmember) {
|
|
next unless $term;
|
|
$searchmember =~ s/\*/%/g; # * is replaced with % for sql
|
|
$term .= '%' # end with anything
|
|
if $term !~ /%$/;
|
|
$term = "%$term" # begin with anythin unless start_with
|
|
if $term !~ /^%/
|
|
and $searchtype eq "contain";
|
|
push @where_strs_or, "borrowers." . $dbh->quote_identifier($searchfield) . " LIKE ?";
|
|
push @where_args, $term;
|
|
}
|
|
}
|
|
push @where_strs, '('. join (' OR ', @where_strs_or) . ')'
|
|
if @where_strs_or;
|
|
|
|
my $where;
|
|
$where = " WHERE " . join (" AND ", @where_strs) if @where_strs;
|
|
my $orderby = dt_build_orderby($dt_params);
|
|
|
|
my $limit;
|
|
# If iDisplayLength == -1, we want to display all patrons
|
|
if ( $dt_params->{iDisplayLength} > -1 ) {
|
|
# In order to avoid sql injection
|
|
$dt_params->{iDisplayStart} =~ s/\D//g;
|
|
$dt_params->{iDisplayLength} =~ s/\D//g;
|
|
$dt_params->{iDisplayStart} //= 0;
|
|
$dt_params->{iDisplayLength} //= 20;
|
|
$limit = "LIMIT $dt_params->{iDisplayStart},$dt_params->{iDisplayLength}";
|
|
}
|
|
|
|
my $query = join(
|
|
" ",
|
|
($select ? $select : ""),
|
|
($from ? $from : ""),
|
|
($where ? $where : ""),
|
|
($orderby ? $orderby : ""),
|
|
($limit ? $limit : "")
|
|
);
|
|
my $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 the iTotalRecords DataTable variable
|
|
$query = "SELECT COUNT(borrowers.borrowernumber) FROM borrowers";
|
|
$sth = $dbh->prepare($query);
|
|
$sth->execute;
|
|
($iTotalRecords) = $sth->fetchrow_array;
|
|
|
|
# Get some information on patrons
|
|
foreach my $patron (@$patrons) {
|
|
($patron->{overdues}, $patron->{issues}, $patron->{fines}) =
|
|
GetMemberIssuesAndFines($patron->{borrowernumber});
|
|
if($patron->{dateexpiry} and $patron->{dateexpiry} ne '0000-00-00') {
|
|
$patron->{dateexpiry} = C4::Dates->new($patron->{dateexpiry}, "iso")->output();
|
|
} else {
|
|
$patron->{dateexpiry} = '';
|
|
}
|
|
$patron->{fines} = sprintf("%.2f", $patron->{fines} || 0);
|
|
}
|
|
|
|
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 'contain' or 'start_with'. Used for the searchmember parameter.
|
|
|
|
=item searchfieldstype
|
|
|
|
Can be 'standard', '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
|
|
|
|
Copyright 2013 BibLibre
|
|
|
|
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.
|