Fix for Bug 6260 - Missing space in availability information of OPAC XSLT results
[koha.git] / C4 / Members.pm
index ace939d..ce694e5 100644 (file)
@@ -45,6 +45,7 @@ BEGIN {
                &Search
                &SearchMember 
                &GetMemberDetails
+        &GetMemberRelatives
                &GetMember
 
                &GetGuarantees 
@@ -70,6 +71,8 @@ BEGIN {
     &PutPatronImage
     &RmPatronImage
 
+                &GetHideLostItemsPreference
+
                &IsMemberBlocked
                &GetMemberAccountRecords
                &GetBorNotifyAcctRecord
@@ -210,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 - .= <<EOT;
         }
@@ -580,6 +589,46 @@ sub GetMember {
     return;
 }
 
+=head2 GetMemberRelatives
+
+ @borrowernumbers = GetMemberRelatives($borrowernumber);
+
+ C<GetMemberRelatives> 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
 
@@ -723,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;
 }
 
@@ -743,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
 
@@ -751,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");
@@ -761,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'};
 }
 
@@ -793,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++;
 
@@ -961,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.
 
@@ -974,14 +1032,22 @@ The keys include C<biblioitems> 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.*,
@@ -998,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) {
@@ -1055,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";
@@ -1107,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
@@ -1201,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);
@@ -1231,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];
 }
 
 
@@ -1554,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{},
         };
     }
 
@@ -1807,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);