X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=C4%2FMembers.pm;h=ce694e590ec1d2daa89effe9713ec41a8a65be35;hb=6a1b7a3321c28534579ac889c9ebf08aa13335c6;hp=00716cd0f713ce2680690e10934d6a5fccc29cbc;hpb=5278aa0ca4bb5110741b792b527725280a3ee902;p=koha.git diff --git a/C4/Members.pm b/C4/Members.pm index 00716cd0f7..ce694e590e 100644 --- a/C4/Members.pm +++ b/C4/Members.pm @@ -1,6 +1,7 @@ package C4::Members; # Copyright 2000-2003 Katipo Communications +# Copyright 2010 BibLibre # # This file is part of Koha. # @@ -44,6 +45,7 @@ BEGIN { &Search &SearchMember &GetMemberDetails + &GetMemberRelatives &GetMember &GetGuarantees @@ -69,6 +71,8 @@ BEGIN { &PutPatronImage &RmPatronImage + &GetHideLostItemsPreference + &IsMemberBlocked &GetMemberAccountRecords &GetBorNotifyAcctRecord @@ -93,6 +97,7 @@ BEGIN { push @EXPORT, qw( &ModMember &changepassword + &ModPrivacy ); #Delete data @@ -208,22 +213,28 @@ sub SearchMember { $query.=" borrowers.branchcode =".$dbh->quote(C4::Context->userenv->{'branch'})." AND " unless (C4::Context->userenv->{'branch'} eq "insecure"); } } - $query.="((surname LIKE ? OR surname LIKE ? - OR firstname LIKE ? OR firstname LIKE ? - OR othernames LIKE ? OR othernames LIKE ?) + $query.="((surname LIKE ? OR (surname LIKE ? AND surname REGEXP ?) + OR firstname LIKE ? OR (firstname LIKE ? AND firstname REGEXP ?) + OR othernames LIKE ? OR (othernames LIKE ? AND othernames REGEXP ?)) " . ($category_type?" AND category_type = ".$dbh->quote($category_type):""); + my $regex = '[[:punct:][:space:]]'.$data[0]; @bind = ( - "$data[0]%", "% $data[0]%", "$data[0]%", "% $data[0]%", - "$data[0]%", "% $data[0]%" + "$data[0]%", "%$data[0]%", $regex, + "$data[0]%", "%$data[0]%", $regex, + "$data[0]%", "%$data[0]%", $regex ); for ( my $i = 1 ; $i < $count ; $i++ ) { - $query = $query . " AND (" . " surname LIKE ? OR surname LIKE ? - OR firstname LIKE ? OR firstname LIKE ? - OR othernames LIKE ? OR othernames LIKE ?)"; + $query = $query . " AND (" . " surname LIKE ? OR (surname LIKE ? AND surname REGEXP ?) + OR firstname LIKE ? OR (firstname LIKE ? AND firstname REGEXP ?) + OR othernames LIKE ? OR (othernames LIKE ? AND othernames REGEXP ?))"; + $regex = '[[:punct:][:space:]]'.$data[$i]; push( @bind, - "$data[$i]%", "% $data[$i]%", "$data[$i]%", - "% $data[$i]%", "$data[$i]%", "% $data[$i]%" ); + "$data[$i]%", "%$data[$i]%", $regex, + "$data[$i]%", "%$data[$i]%", $regex, + "$data[$i]%", "%$data[$i]%", $regex + ); + # FIXME - .= < is a string telling the type of search you want todo : start_with =cut sub Search { - my ($filter,$orderby, $limit, $columns_out, $search_on_fields,$searchtype) = @_; - my @filters; - if (ref($filter) eq "ARRAY"){ - push @filters,@$filter; - } - else { - push @filters,$filter; - } - if (C4::Context->preference('ExtendedPatronAttributes')) { - my $matching_records = C4::Members::Attributes::SearchIdMatchingAttribute($filter); - push @filters,@$matching_records; - } - $searchtype||="start_with"; - my $data=SearchInTable("borrowers",\@filters,$orderby,$limit,$columns_out,$search_on_fields,$searchtype); - - return ( $data ); + my ( $filter, $orderby, $limit, $columns_out, $search_on_fields, $searchtype ) = @_; + my @filters; + my %filtersmatching_record; + my @finalfilter; + if ( ref($filter) eq "ARRAY" ) { + push @filters, @$filter; + } else { + push @filters, $filter; + } + if ( C4::Context->preference('ExtendedPatronAttributes') ) { + my $matching_records = C4::Members::Attributes::SearchIdMatchingAttribute($filter); + if(scalar(@$matching_records)>0) { + foreach my $matching_record (@$matching_records) { + $filtersmatching_record{$$matching_record[0]}=1; + } + foreach my $k (keys(%filtersmatching_record)) { + push @filters, {"borrowernumber"=>$k}; + } + } + } + $searchtype ||= "start_with"; + push @finalfilter, \@filters; + my $data = SearchInTable( "borrowers", \@finalfilter, $orderby, $limit, $columns_out, $search_on_fields, $searchtype ); + return ($data); } =head2 GetMemberDetails @@ -443,7 +462,7 @@ sub patronflags { my $noissuescharge = C4::Context->preference("noissuescharge") || 5; $flaginfo{'message'} = sprintf "Patron owes \$%.02f", $amount; $flaginfo{'amount'} = sprintf "%.02f", $amount; - if ( $amount > $noissuescharge ) { + if ( $amount > $noissuescharge && !C4::Context->preference("AllowFineOverride") ) { $flaginfo{'noissues'} = 1; } $flags{'CHARGES'} = \%flaginfo; @@ -570,6 +589,46 @@ sub GetMember { return; } +=head2 GetMemberRelatives + + @borrowernumbers = GetMemberRelatives($borrowernumber); + + C returns a borrowersnumber's list of guarantor/guarantees of the member given in parameter + +=cut +sub GetMemberRelatives { + my $borrowernumber = shift; + my $dbh = C4::Context->dbh; + my @glist; + + # Getting guarantor + my $query = "SELECT guarantorid FROM borrowers WHERE borrowernumber=?"; + my $sth = $dbh->prepare($query); + $sth->execute($borrowernumber); + my $data = $sth->fetchrow_arrayref(); + push @glist, $data->[0] if $data->[0]; + my $guarantor = $data->[0] if $data->[0]; + + # Getting guarantees + $query = "SELECT borrowernumber FROM borrowers WHERE guarantorid=?"; + $sth = $dbh->prepare($query); + $sth->execute($borrowernumber); + while ($data = $sth->fetchrow_arrayref()) { + push @glist, $data->[0]; + } + + # Getting sibling guarantees + if ($guarantor) { + $query = "SELECT borrowernumber FROM borrowers WHERE guarantorid=?"; + $sth = $dbh->prepare($query); + $sth->execute($guarantor); + while ($data = $sth->fetchrow_arrayref()) { + push @glist, $data->[0] if ($data->[0] != $borrowernumber); + } + } + + return @glist; +} =head2 IsMemberBlocked @@ -713,17 +772,17 @@ sub ModMember { } } my $execute_success=UpdateInTable("borrowers",\%data); -# ok if its an adult (type) it may have borrowers that depend on it as a guarantor -# so when we update information for an adult we should check for guarantees and update the relevant part -# of their records, ie addresses and phone numbers - my $borrowercategory= GetBorrowercategory( $data{'category_type'} ); - if ( exists $borrowercategory->{'category_type'} && $borrowercategory->{'category_type'} eq ('A' || 'S') ) { - # is adult check guarantees; - UpdateGuarantees(%data); + if ($execute_success) { # only proceed if the update was a success + # ok if its an adult (type) it may have borrowers that depend on it as a guarantor + # so when we update information for an adult we should check for guarantees and update the relevant part + # of their records, ie addresses and phone numbers + my $borrowercategory= GetBorrowercategory( $data{'category_type'} ); + if ( exists $borrowercategory->{'category_type'} && $borrowercategory->{'category_type'} eq ('A' || 'S') ) { + # is adult check guarantees; + UpdateGuarantees(%data); + } + logaction("MEMBERS", "MODIFY", $data{'borrowernumber'}, "UPDATE (executed w/ arg: $data{'borrowernumber'})") if C4::Context->preference("BorrowersLog"); } - logaction("MEMBERS", "MODIFY", $data{'borrowernumber'}, "UPDATE (executed w/ arg: $data{'borrowernumber'})") - if C4::Context->preference("BorrowersLog"); - return $execute_success; } @@ -733,7 +792,9 @@ sub ModMember { $borrowernumber = &AddMember(%borrower); insert new borrower into table -Returns the borrowernumber +Returns the borrowernumber upon success + +Returns as undef upon any db error without further processing =cut @@ -741,8 +802,10 @@ Returns the borrowernumber sub AddMember { my (%data) = @_; my $dbh = C4::Context->dbh; - $data{'password'} = '!' if (not $data{'password'} and $data{'userid'}); - $data{'password'} = md5_base64( $data{'password'} ) if $data{'password'}; + # generate a proper login if none provided + $data{'userid'} = Generate_Userid($data{'borrowernumber'}, $data{'firstname'}, $data{'surname'}) if $data{'userid'} eq ''; + # create a disabled account if no password provided + $data{'password'} = ($data{'password'})? md5_base64($data{'password'}) : '!'; $data{'borrowernumber'}=InsertInTable("borrowers",\%data); # mysql_insertid is probably bad. not necessarily accurate and mysql-specific at best. logaction("MEMBERS", "CREATE", $data{'borrowernumber'}, "") if C4::Context->preference("BorrowersLog"); @@ -751,10 +814,15 @@ sub AddMember { my $sth = $dbh->prepare("SELECT enrolmentfee FROM categories WHERE categorycode=?"); $sth->execute($data{'categorycode'}); my ($enrolmentfee) = $sth->fetchrow; + if ($sth->err) { + warn sprintf('Database returned the following error: %s', $sth->errstr); + return; + } if ($enrolmentfee && $enrolmentfee > 0) { # insert fee in patron debts manualinvoice($data{'borrowernumber'}, '', '', 'A', $enrolmentfee); } + return $data{'borrowernumber'}; } @@ -783,7 +851,7 @@ sub Generate_Userid { do { $firstname =~ s/[[:digit:][:space:][:blank:][:punct:][:cntrl:]]//g; $surname =~ s/[[:digit:][:space:][:blank:][:punct:][:cntrl:]]//g; - $newuid = lc("$firstname.$surname"); + $newuid = lc(($firstname)? "$firstname.$surname" : $surname); $newuid .= $offset unless $offset == 0; $offset++; @@ -951,7 +1019,7 @@ sub UpdateGuarantees { } =head2 GetPendingIssues - my $issues = &GetPendingIssues($borrowernumber); + my $issues = &GetPendingIssues(@borrowernumber); Looks up what the patron with the given borrowernumber has borrowed. @@ -964,14 +1032,22 @@ The keys include C fields except marc and marcxml. #' sub GetPendingIssues { - my ($borrowernumber) = @_; + my (@borrowernumbers) = @_; + + # Borrowers part of the query + my $bquery = ''; + for (my $i = 0; $i < @borrowernumbers; $i++) { + $bquery .= " borrowernumber = ?"; + $bquery .= " OR" if ($i < (scalar(@borrowernumbers) - 1)); + } + # must avoid biblioitems.* to prevent large marc and marcxml fields from killing performance # FIXME: namespace collision: each table has "timestamp" fields. Which one is "timestamp" ? # FIXME: circ/ciculation.pl tries to sort by timestamp! # FIXME: C4::Print::printslip tries to sort by timestamp! # FIXME: namespace collision: other collisions possible. # FIXME: most of this data isn't really being used by callers. - my $sth = C4::Context->dbh->prepare( + my $query = "SELECT issues.*, items.*, biblio.*, @@ -988,16 +1064,19 @@ sub GetPendingIssues { biblioitems.url, issues.timestamp AS timestamp, issues.renewals AS renewals, + issues.borrowernumber AS borrowernumber, items.renewals AS totalrenewals FROM issues LEFT JOIN items ON items.itemnumber = issues.itemnumber LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber WHERE - borrowernumber=? + $bquery ORDER BY issues.issuedate" - ); - $sth->execute($borrowernumber); + ; + + my $sth = C4::Context->dbh->prepare($query); + $sth->execute(@borrowernumbers); my $data = $sth->fetchall_arrayref({}); my $today = C4::Dates->new->output('iso'); foreach (@$data) { @@ -1045,7 +1124,7 @@ sub GetAllIssues { LEFT JOIN items on items.itemnumber=old_issues.itemnumber LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber LEFT JOIN biblioitems ON items.biblioitemnumber=biblioitems.biblioitemnumber - WHERE borrowernumber=? + WHERE borrowernumber=? AND old_issues.itemnumber IS NOT NULL order by $order"; if ( $limit != 0 ) { $query .= " limit $limit"; @@ -1097,9 +1176,11 @@ sub GetMemberAccountRecords { $sth->execute( @bind ); my $total = 0; while ( my $data = $sth->fetchrow_hashref ) { - my $biblio = GetBiblioFromItemNumber($data->{itemnumber}) if $data->{itemnumber}; - $data->{biblionumber} = $biblio->{biblionumber}; - $data->{title} = $biblio->{title}; + if ( $data->{itemnumber} ) { + my $biblio = GetBiblioFromItemNumber( $data->{itemnumber} ); + $data->{biblionumber} = $biblio->{biblionumber}; + $data->{title} = $biblio->{title}; + } $acctlines[$numlines] = $data; $numlines++; $total += int(1000 * $data->{'amountoutstanding'}); # convert float to integer to avoid round-off errors @@ -1191,6 +1272,8 @@ sub checkuniquemember { sub checkcardnumber { my ($cardnumber,$borrowernumber) = @_; + # If cardnumber is null, we assume they're allowed. + return 0 if !defined($cardnumber); my $dbh = C4::Context->dbh; my $query = "SELECT * FROM borrowers WHERE cardnumber=?"; $query .= " AND borrowernumber <> ?" if ($borrowernumber); @@ -1221,10 +1304,10 @@ sub getzipnamecity { my $dbh = C4::Context->dbh; my $sth = $dbh->prepare( - "select city_name,city_zipcode from cities where cityid=? "); + "select city_name,city_state,city_zipcode,city_country from cities where cityid=? "); $sth->execute($cityid); my @data = $sth->fetchrow; - return $data[0], $data[1]; + return $data[0], $data[1], $data[2], $data[3]; } @@ -1544,13 +1627,15 @@ sub GetCities { my $dbh = C4::Context->dbh; my $city_arr = $dbh->selectall_arrayref( - q|SELECT cityid,city_zipcode,city_name FROM cities ORDER BY city_name|, + q|SELECT cityid,city_zipcode,city_name,city_state,city_country FROM cities ORDER BY city_name|, { Slice => {} }); if ( @{$city_arr} ) { unshift @{$city_arr}, { city_zipcode => q{}, city_name => q{}, cityid => q{}, + city_state => q{}, + city_country => q{}, }; } @@ -1797,6 +1882,25 @@ sub RmPatronImage { return $dberror; } +=head2 GetHideLostItemsPreference + + $hidelostitemspref = &GetHideLostItemsPreference($borrowernumber); + +Returns the HideLostItems preference for the patron category of the supplied borrowernumber +C<&$hidelostitemspref>return value of function, 0 or 1 + +=cut + +sub GetHideLostItemsPreference { + my ($borrowernumber) = @_; + my $dbh = C4::Context->dbh; + my $query = "SELECT hidelostitems FROM borrowers,categories WHERE borrowers.categorycode = categories.categorycode AND borrowernumber = ?"; + my $sth = $dbh->prepare($query); + $sth->execute($borrowernumber); + my $hidelostitems = $sth->fetchrow; + return $hidelostitems; +} + =head2 GetRoadTypeDetails (OUEST-PROVENCE) ($roadtype) = &GetRoadTypeDetails($roadtypeid); @@ -2022,6 +2126,31 @@ sub DebarMember { } +=head2 ModPrivacy + +=over 4 + +my $success = ModPrivacy( $borrowernumber, $privacy ); + +Update the privacy of a patron. + +return : +true on success, false on failure + +=back + +=cut + +sub ModPrivacy { + my $borrowernumber = shift; + my $privacy = shift; + return unless defined $borrowernumber; + return unless $borrowernumber =~ /^\d+$/; + + return ModMember( borrowernumber => $borrowernumber, + privacy => $privacy ); +} + =head2 AddMessage AddMessage( $borrowernumber, $message_type, $message, $branchcode );