d03e25783d81a17c8d5793b744ebfecb00169a5c
[koha.git] / C4 / Utils / DataTables / Members.pm
1 package C4::Utils::DataTables::Members;
2
3 use Modern::Perl;
4 use C4::Context;
5 use C4::Members qw/GetMemberIssuesAndFines/;
6 use C4::Utils::DataTables;
7 use Koha::DateUtils;
8
9 sub search {
10     my ( $params ) = @_;
11     my $searchmember = $params->{searchmember};
12     my $firstletter = $params->{firstletter};
13     my $categorycode = $params->{categorycode};
14     my $branchcode = $params->{branchcode};
15     my $searchtype = $params->{searchtype} || 'contain';
16     my $searchfieldstype = $params->{searchfieldstype} || 'standard';
17     my $dt_params = $params->{dt_params};
18
19     unless ( $searchmember ) {
20         $searchmember = $dt_params->{sSearch} // '';
21     }
22
23     my ($sth, $query, $iTotalRecords, $iTotalDisplayRecords);
24     my $dbh = C4::Context->dbh;
25     # Get the iTotalRecords DataTable variable
26     $query = "SELECT COUNT(borrowers.borrowernumber) FROM borrowers";
27     $sth = $dbh->prepare($query);
28     $sth->execute;
29     ($iTotalRecords) = $sth->fetchrow_array;
30
31     if ( $searchfieldstype eq 'dateofbirth' ) {
32         # Return an empty list if the date of birth is not correctly formatted
33         $searchmember = eval { output_pref( { str => $searchmember, dateformat => 'iso', dateonly => 1 } ); };
34         if ( $@ or not $searchmember ) {
35             return {
36                 iTotalRecords        => 0,
37                 iTotalDisplayRecords => 0,
38                 patrons              => [],
39             };
40         }
41     }
42
43     # If branches are independent and user is not superlibrarian
44     # The search has to be only on the user branch
45     if ( C4::Context::only_my_library ) {
46         my $userenv = C4::Context->userenv;
47         $branchcode = $userenv->{'branch'};
48
49     }
50
51     my $select = "SELECT
52         borrowers.borrowernumber, borrowers.surname, borrowers.firstname,
53         borrowers.streetnumber, borrowers.streettype, borrowers.address,
54         borrowers.address2, borrowers.city, borrowers.state, borrowers.zipcode,
55         borrowers.country, cardnumber, borrowers.dateexpiry,
56         borrowers.borrowernotes, borrowers.branchcode, borrowers.email,
57         borrowers.userid, borrowers.dateofbirth, borrowers.categorycode,
58         categories.description AS category_description, categories.category_type,
59         branches.branchname";
60     my $from = "FROM borrowers
61         LEFT JOIN branches ON borrowers.branchcode = branches.branchcode
62         LEFT JOIN categories ON borrowers.categorycode = categories.categorycode";
63     my @where_args;
64     my @where_strs;
65     if(defined $firstletter and $firstletter ne '') {
66         push @where_strs, "borrowers.surname LIKE ?";
67         push @where_args, "$firstletter%";
68     }
69     if(defined $categorycode and $categorycode ne '') {
70         push @where_strs, "borrowers.categorycode = ?";
71         push @where_args, $categorycode;
72     }
73     if(defined $branchcode and $branchcode ne '') {
74         push @where_strs, "borrowers.branchcode = ?";
75         push @where_args, $branchcode;
76     }
77
78     my $searchfields = {
79         standard => C4::Context->preference('DefaultPatronSearchFields') || 'surname,firstname,othernames,cardnumber,userid',
80         surname => 'surname',
81         email => 'email,emailpro,B_email',
82         borrowernumber => 'borrowernumber',
83         userid => 'userid',
84         phone => 'phone,phonepro,B_phone,altcontactphone,mobile',
85         address => 'streettype,address,address2,city,state,zipcode,country',
86         dateofbirth => 'dateofbirth',
87         sort1 => 'sort1',
88         sort2 => 'sort2',
89     };
90
91     # * is replaced with % for sql
92     $searchmember =~ s/\*/%/g;
93
94     # split into search terms
95     my @terms;
96     # consider coma as space
97     $searchmember =~ s/,/ /g;
98     if ( $searchtype eq 'contain' ) {
99        @terms = split / /, $searchmember;
100     } else {
101        @terms = ($searchmember);
102     }
103
104     foreach my $term (@terms) {
105         next unless $term;
106
107         my $term_dt = eval { local $SIG{__WARN__} = {}; output_pref( { str => $term, dateonly => 1, dateformat => 'sql' } ); };
108
109         if ($term_dt) {
110             $term = $term_dt;
111         } else {
112             $term .= '%'    # end with anything
113               if $term !~ /%$/;
114             $term = "%$term"    # begin with anythin unless start_with
115               if $searchtype eq 'contain' && $term !~ /^%/;
116         }
117
118         my @where_strs_or;
119         for my $searchfield ( split /,/, $searchfields->{$searchfieldstype} ) {
120             push @where_strs_or, "borrowers." . $dbh->quote_identifier($searchfield) . " LIKE ?";
121             push @where_args, $term;
122         }
123
124         if ( $searchfieldstype eq 'standard' and C4::Context->preference('ExtendedPatronAttributes') and $searchmember ) {
125             my $matching_borrowernumbers = C4::Members::Attributes::SearchIdMatchingAttribute($searchmember);
126
127             for my $borrowernumber ( @$matching_borrowernumbers ) {
128                 push @where_strs_or, "borrowers.borrowernumber = ?";
129                 push @where_args, $borrowernumber;
130             }
131         }
132
133         push @where_strs, '('. join (' OR ', @where_strs_or) . ')'
134             if @where_strs_or;
135     }
136
137     my $where;
138     $where = " WHERE " . join (" AND ", @where_strs) if @where_strs;
139     my $orderby = dt_build_orderby($dt_params);
140
141     my $limit;
142     # If iDisplayLength == -1, we want to display all patrons
143     if ( !$dt_params->{iDisplayLength} || $dt_params->{iDisplayLength} > -1 ) {
144         # In order to avoid sql injection
145         $dt_params->{iDisplayStart} =~ s/\D//g if defined($dt_params->{iDisplayStart});
146         $dt_params->{iDisplayLength} =~ s/\D//g if defined($dt_params->{iDisplayLength});
147         $dt_params->{iDisplayStart} //= 0;
148         $dt_params->{iDisplayLength} //= 20;
149         $limit = "LIMIT $dt_params->{iDisplayStart},$dt_params->{iDisplayLength}";
150     }
151
152     $query = join(
153         " ",
154         ($select ? $select : ""),
155         ($from ? $from : ""),
156         ($where ? $where : ""),
157         ($orderby ? $orderby : ""),
158         ($limit ? $limit : "")
159     );
160     $sth = $dbh->prepare($query);
161     $sth->execute(@where_args);
162     my $patrons = $sth->fetchall_arrayref({});
163
164     # Get the iTotalDisplayRecords DataTable variable
165     $query = "SELECT COUNT(borrowers.borrowernumber) " . $from . ($where ? $where : "");
166     $sth = $dbh->prepare($query);
167     $sth->execute(@where_args);
168     ($iTotalDisplayRecords) = $sth->fetchrow_array;
169
170     # Get some information on patrons
171     foreach my $patron (@$patrons) {
172         ($patron->{overdues}, $patron->{issues}, $patron->{fines}) =
173             GetMemberIssuesAndFines($patron->{borrowernumber});
174         if($patron->{dateexpiry} and $patron->{dateexpiry} ne '0000-00-00') {
175             $patron->{dateexpiry} = output_pref( { dt => dt_from_string( $patron->{dateexpiry}, 'iso'), dateonly => 1} );
176         } else {
177             $patron->{dateexpiry} = '';
178         }
179         $patron->{fines} = sprintf("%.2f", $patron->{fines} || 0);
180     }
181
182     return {
183         iTotalRecords => $iTotalRecords,
184         iTotalDisplayRecords => $iTotalDisplayRecords,
185         patrons => $patrons
186     }
187 }
188
189 1;
190 __END__
191
192 =head1 NAME
193
194 C4::Utils::DataTables::Members - module for using DataTables with patrons
195
196 =head1 SYNOPSIS
197
198 This module provides (one for the moment) routines used by the patrons search
199
200 =head2 FUNCTIONS
201
202 =head3 search
203
204     my $dt_infos = C4::Utils::DataTables::Members->search($params);
205
206 $params is a hashref with some keys:
207
208 =over 4
209
210 =item searchmember
211
212   String to search in the borrowers sql table
213
214 =item firstletter
215
216   Introduced to contain 1 letter but can contain more.
217   The search will done on the borrowers.surname field
218
219 =item categorycode
220
221   Search patrons with this categorycode
222
223 =item branchcode
224
225   Search patrons with this branchcode
226
227 =item searchtype
228
229   Can be 'start_with' or 'contain' (default value). Used for the searchmember parameter.
230
231 =item searchfieldstype
232
233   Can be 'standard' (default value), 'email', 'borrowernumber', 'phone', 'address' or 'dateofbirth', 'sort1', 'sort2'
234
235 =item dt_params
236
237   Is the reference of C4::Utils::DataTables::dt_get_params($input);
238
239 =cut
240
241 =back
242
243 =head1 LICENSE
244
245 This file is part of Koha.
246
247 Copyright 2013 BibLibre
248
249 Koha is free software; you can redistribute it and/or modify it
250 under the terms of the GNU General Public License as published by
251 the Free Software Foundation; either version 3 of the License, or
252 (at your option) any later version.
253
254 Koha is distributed in the hope that it will be useful, but
255 WITHOUT ANY WARRANTY; without even the implied warranty of
256 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
257 GNU General Public License for more details.
258
259 You should have received a copy of the GNU General Public License
260 along with Koha; if not, see <http://www.gnu.org/licenses>.