Bug 13601: get rid of DateTime::Format::DateParse
[koha.git] / C4 / Members.pm
index f58bb76..a98d16f 100644 (file)
@@ -33,13 +33,19 @@ use C4::Accounts;
 use C4::Biblio;
 use C4::Letters;
 use C4::SQLHelper qw(InsertInTable UpdateInTable SearchInTable);
-use C4::Members::Attributes qw(SearchIdMatchingAttribute);
+use C4::Members::Attributes qw(SearchIdMatchingAttribute UpdateBorrowerAttribute);
 use C4::NewsChannels; #get slip news
 use DateTime;
-use DateTime::Format::DateParse;
+use Koha::Database;
 use Koha::DateUtils;
+use Koha::Borrower::Debarments qw(IsDebarred);
 use Text::Unaccent qw( unac_string );
-use C4::Auth qw(hash_password);
+use Koha::AuthUtils qw(hash_password);
+use Koha::Database;
+use Module::Load;
+if ( C4::Context->preference('NorwegianPatronDBEnable') && C4::Context->preference('NorwegianPatronDBEnable') == 1 ) {
+    load Koha::NorwegianPatronDB, qw( NLUpdateHashedPIN NLEncryptPIN NLSync );
+}
 
 our ($VERSION,@ISA,@EXPORT,@EXPORT_OK,$debug);
 
@@ -61,7 +67,6 @@ BEGIN {
         &GetPendingIssues
         &GetAllIssues
 
-        &get_institutions
         &getzipnamecity
         &getidcity
 
@@ -70,8 +75,6 @@ BEGIN {
 
         &GetAge
         &GetCities
-        &GetRoadTypes
-        &GetRoadTypeDetails
         &GetSortDetails
         &GetTitles
 
@@ -103,6 +106,8 @@ BEGIN {
 
         &IssueSlip
         GetBorrowersWithEmail
+
+        HasOverdues
     );
 
     #Modify data
@@ -121,7 +126,6 @@ BEGIN {
     push @EXPORT, qw(
         &AddMember
         &AddMember_Opac
-        &add_member_orgs
         &MoveMemberToDeleted
         &ExtendMemberSubscriptionTo
     );
@@ -198,7 +202,7 @@ sub _express_member_find {
 }
 
 sub Search {
-    my ( $filter, $orderby, $limit, $columns_out, $search_on_fields, $searchtype ) = @_;
+    my ( $filter, $orderby, $limit, $columns_out, $search_on_fields, $searchtype, $not_attributes ) = @_;
 
     my $search_string;
     my $found_borrower;
@@ -232,7 +236,7 @@ sub Search {
         }
     }
 
-    if ( !$found_borrower && C4::Context->preference('ExtendedPatronAttributes') && $search_string ) {
+    if ( !$found_borrower && C4::Context->preference('ExtendedPatronAttributes') && $search_string && !$not_attributes ) {
         my $matching_records = C4::Members::Attributes::SearchIdMatchingAttribute($search_string);
         if(scalar(@$matching_records)>0) {
             if ( my $fr = ref $filter ) {
@@ -259,7 +263,7 @@ sub Search {
     if ( C4::Context->preference("IndependentBranches") ) { # && !$showallbranches){
         if ( my $userenv = C4::Context->userenv ) {
             my $branch =  $userenv->{'branch'};
-            if ( ($userenv->{flags} % 2 !=1) && $branch ){
+            if ( !C4::Context->IsSuperLibrarian() && $branch ){
                 if (my $fr = ref $filter) {
                     if ( $fr eq "HASH" ) {
                         $filter->{branchcode} = $branch;
@@ -274,7 +278,7 @@ sub Search {
                 else {
                     $filter = { '' => $filter, branchcode => $branch };
                 }
-            }      
+            }
         }
     }
 
@@ -324,18 +328,39 @@ sub GetMemberDetails {
     my $query;
     my $sth;
     if ($borrowernumber) {
-        $sth = $dbh->prepare("SELECT borrowers.*,category_type,categories.description,reservefee,enrolmentperiod FROM borrowers LEFT JOIN categories ON borrowers.categorycode=categories.categorycode WHERE  borrowernumber=?");
+        $sth = $dbh->prepare("
+            SELECT borrowers.*,
+                   category_type,
+                   categories.description,
+                   categories.BlockExpiredPatronOpacActions,
+                   reservefee,
+                   enrolmentperiod
+            FROM borrowers
+            LEFT JOIN categories ON borrowers.categorycode=categories.categorycode
+            WHERE borrowernumber = ?
+        ");
         $sth->execute($borrowernumber);
     }
     elsif ($cardnumber) {
-        $sth = $dbh->prepare("SELECT borrowers.*,category_type,categories.description,reservefee,enrolmentperiod FROM borrowers LEFT JOIN categories ON borrowers.categorycode=categories.categorycode WHERE cardnumber=?");
+        $sth = $dbh->prepare("
+            SELECT borrowers.*,
+                   category_type,
+                   categories.description,
+                   categories.BlockExpiredPatronOpacActions,
+                   reservefee,
+                   enrolmentperiod
+            FROM borrowers
+            LEFT JOIN categories ON borrowers.categorycode = categories.categorycode
+            WHERE cardnumber = ?
+        ");
         $sth->execute($cardnumber);
     }
     else {
         return;
     }
     my $borrower = $sth->fetchrow_hashref;
-    my ($amount) = GetMemberAccountRecords( $borrowernumber);
+    return unless $borrower;
+    my ($amount) = GetMemberAccountRecords($borrower->{borrowernumber});
     $borrower->{'amountoutstanding'} = $amount;
     # FIXME - patronflags calls GetMemberAccountRecords... just have patronflags return $amount
     my $flags = patronflags( $borrower);
@@ -361,6 +386,18 @@ sub GetMemberDetails {
         $borrower->{'showname'} = $borrower->{'firstname'};
     }
 
+    # Handle setting the true behavior for BlockExpiredPatronOpacActions
+    $borrower->{'BlockExpiredPatronOpacActions'} =
+      C4::Context->preference('BlockExpiredPatronOpacActions')
+      if ( $borrower->{'BlockExpiredPatronOpacActions'} == -1 );
+
+    $borrower->{'is_expired'} = 0;
+    $borrower->{'is_expired'} = 1 if
+      defined($borrower->{dateexpiry}) &&
+      $borrower->{'dateexpiry'} ne '0000-00-00' &&
+      Date_to_Days( Today() ) >
+      Date_to_Days( split /-/, $borrower->{'dateexpiry'} );
+
     return ($borrower);    #, $flags, $accessflagshash);
 }
 
@@ -572,7 +609,8 @@ sub GetMember {
 
  C<GetMemberRelatives> returns a borrowersnumber's list of guarantor/guarantees of the member given in parameter
 
-=cut 
+=cut
+
 sub GetMemberRelatives {
     my $borrowernumber = shift;
     my $dbh = C4::Context->dbh;
@@ -617,7 +655,8 @@ that would block circulation privileges.
 
 C<$block_status> can have the following values:
 
-1 if the patron has outstanding fine days, in which case C<$count> is the number of them
+1 if the patron has outstanding fine days or a manual debarment, in which case
+C<$count> is the expiration date (9999-12-31 for indefinite)
 
 -1 if the patron has overdue items, in which case C<$count> is the number of them
 
@@ -636,7 +675,7 @@ sub IsMemberBlocked {
     my $borrowernumber = shift;
     my $dbh            = C4::Context->dbh;
 
-    my $blockeddate = CheckBorrowerDebarred($borrowernumber);
+    my $blockeddate = Koha::Borrower::Debarments::IsDebarred($borrowernumber);
 
     return ( 1, $blockeddate ) if $blockeddate;
 
@@ -750,6 +789,10 @@ sub ModMember {
         if ($data{password} eq '****' or $data{password} eq '') {
             delete $data{password};
         } else {
+            if ( C4::Context->preference('NorwegianPatronDBEnable') && C4::Context->preference('NorwegianPatronDBEnable') == 1 ) {
+                # Update the hashed PIN in borrower_sync.hashed_pin, before Koha hashes it
+                NLUpdateHashedPIN( $data{'borrowernumber'}, $data{password} );
+            }
             $data{password} = hash_password($data{password});
         }
     }
@@ -770,6 +813,25 @@ sub ModMember {
             AddEnrolmentFeeIfNeeded( $data{categorycode}, $data{borrowernumber} );
         }
 
+        # If NorwegianPatronDBEnable is enabled, we set syncstatus to something that a
+        # cronjob will use for syncing with NL
+        if ( C4::Context->preference('NorwegianPatronDBEnable') && C4::Context->preference('NorwegianPatronDBEnable') == 1 ) {
+            my $borrowersync = Koha::Database->new->schema->resultset('BorrowerSync')->find({
+                'synctype'       => 'norwegianpatrondb',
+                'borrowernumber' => $data{'borrowernumber'}
+            });
+            # Do not set to "edited" if syncstatus is "new". We need to sync as new before
+            # we can sync as changed. And the "new sync" will pick up all changes since
+            # the patron was created anyway.
+            if ( $borrowersync->syncstatus ne 'new' && $borrowersync->syncstatus ne 'delete' ) {
+                $borrowersync->update( { 'syncstatus' => 'edited' } );
+            }
+            # Set the value of 'sync'
+            $borrowersync->update( { 'sync' => $data{'sync'} } );
+            # Try to do the live sync
+            NLSync({ 'borrowernumber' => $data{'borrowernumber'} });
+        }
+
         logaction("MEMBERS", "MODIFY", $data{'borrowernumber'}, "UPDATE (executed w/ arg: $data{'borrowernumber'})") if C4::Context->preference("BorrowersLog");
     }
     return $execute_success;
@@ -780,6 +842,11 @@ sub ModMember {
   $borrowernumber = &AddMember(%borrower);
 
 insert new borrower into table
+
+(%borrower keys are database columns. Database columns could be
+different in different versions. Please look into database for correct
+column names.)
+
 Returns the borrowernumber upon success
 
 Returns as undef upon any db error without further processing
@@ -792,7 +859,8 @@ sub AddMember {
     my $dbh = C4::Context->dbh;
 
     # generate a proper login if none provided
-    $data{'userid'} = Generate_Userid($data{'borrowernumber'}, $data{'firstname'}, $data{'surname'}) if $data{'userid'} eq '';
+    $data{'userid'} = Generate_Userid( $data{'borrowernumber'}, $data{'firstname'}, $data{'surname'} )
+      if ( $data{'userid'} eq '' || !Check_Userid( $data{'userid'} ) );
 
     # add expiration date if it isn't already there
     unless ( $data{'dateexpiry'} ) {
@@ -804,10 +872,33 @@ sub AddMember {
         $data{'dateenrolled'} = C4::Dates->new()->output("iso");
     }
 
+    my $patron_category =
+      Koha::Database->new()->schema()->resultset('Category')
+      ->find( $data{'categorycode'} );
+    $data{'privacy'} =
+        $patron_category->default_privacy() eq 'default' ? 1
+      : $patron_category->default_privacy() eq 'never'   ? 2
+      : $patron_category->default_privacy() eq 'forever' ? 0
+      :                                                    undef;
+    # Make a copy of the plain text password for later use
+    my $plain_text_password = $data{'password'};
+
     # create a disabled account if no password provided
     $data{'password'} = ($data{'password'})? hash_password($data{'password'}) : '!';
     $data{'borrowernumber'}=InsertInTable("borrowers",\%data);
 
+    # If NorwegianPatronDBEnable is enabled, we set syncstatus to something that a
+    # cronjob will use for syncing with NL
+    if ( exists $data{'borrowernumber'} && C4::Context->preference('NorwegianPatronDBEnable') && C4::Context->preference('NorwegianPatronDBEnable') == 1 ) {
+        Koha::Database->new->schema->resultset('BorrowerSync')->create({
+            'borrowernumber' => $data{'borrowernumber'},
+            'synctype'       => 'norwegianpatrondb',
+            'sync'           => 1,
+            'syncstatus'     => 'new',
+            'hashed_pin'     => NLEncryptPIN( $plain_text_password ),
+        });
+    }
+
     # mysql_insertid is probably bad.  not necessarily accurate and mysql-specific at best.
     logaction("MEMBERS", "CREATE", $data{'borrowernumber'}, "") if C4::Context->preference("BorrowersLog");
 
@@ -831,18 +922,21 @@ sub AddMember {
 =cut
 
 sub Check_Userid {
-    my ($uid,$member) = @_;
-    my $dbh = C4::Context->dbh;
-    my $sth =
-      $dbh->prepare(
-        "SELECT * FROM borrowers WHERE userid=? AND borrowernumber != ?");
-    $sth->execute( $uid, $member );
-    if ( ( $uid ne '' ) && ( my $row = $sth->fetchrow_hashref ) ) {
-        return 0;
-    }
-    else {
-        return 1;
-    }
+    my ( $uid, $borrowernumber ) = @_;
+
+    return 0 unless ($uid); # userid is a unique column, we should assume NULL is not unique
+
+    return 0 if ( $uid eq C4::Context->config('user') );
+
+    my $rs = Koha::Database->new()->schema()->resultset('Borrower');
+
+    my $params;
+    $params->{userid} = $uid;
+    $params->{borrowernumber} = { '!=' => $borrowernumber } if ($borrowernumber);
+
+    my $count = $rs->count( $params );
+
+    return $count ? 0 : 1;
 }
 
 =head2 Generate_Userid
@@ -1100,14 +1194,13 @@ sub GetPendingIssues {
     my $sth = C4::Context->dbh->prepare($query);
     $sth->execute(@borrowernumbers);
     my $data = $sth->fetchall_arrayref({});
-    my $tz = C4::Context->tz();
-    my $today = DateTime->now( time_zone => $tz);
+    my $today = dt_from_string;
     foreach (@{$data}) {
         if ($_->{issuedate}) {
             $_->{issuedate} = dt_from_string($_->{issuedate}, 'sql');
         }
         $_->{date_due} or next;
-        $_->{date_due} = DateTime::Format::DateParse->parse_datetime($_->{date_due}, $tz->name());
+        $_->{date_due} = dt_from_string($_->{date_due}, 'sql');
         if ( DateTime->compare($_->{date_due}, $today) == -1 ) {
             $_->{overdue} = 1;
         }
@@ -1138,6 +1231,9 @@ C<items> tables of the Koha database.
 sub GetAllIssues {
     my ( $borrowernumber, $order, $limit ) = @_;
 
+    return unless $borrowernumber;
+    $order = 'date_due desc' unless $order;
+
     my $dbh = C4::Context->dbh;
     my $query =
 'SELECT *, issues.timestamp as issuestimestamp, issues.renewals AS renewals,items.renewals AS totalrenewals,items.timestamp AS itemstimestamp
@@ -1224,7 +1320,8 @@ sub GetMemberAccountBalance {
 
     my $ACCOUNT_TYPE_LENGTH = 5; # this is plain ridiculous...
 
-    my @not_fines = ('Res');
+    my @not_fines;
+    push @not_fines, 'Res' unless C4::Context->preference('HoldsInNoissuesCharge');
     push @not_fines, 'Rent' unless C4::Context->preference('RentalsInNoissuesCharge');
     unless ( C4::Context->preference('ManInvInNoissuesCharge') ) {
         my $dbh = C4::Context->dbh;
@@ -1326,26 +1423,60 @@ sub checkuniquemember {
 }
 
 sub checkcardnumber {
-    my ($cardnumber,$borrowernumber) = @_;
+    my ( $cardnumber, $borrowernumber ) = @_;
+
     # If cardnumber is null, we assume they're allowed.
-    return 0 if !defined($cardnumber);
+    return 0 unless defined $cardnumber;
+
     my $dbh = C4::Context->dbh;
     my $query = "SELECT * FROM borrowers WHERE cardnumber=?";
     $query .= " AND borrowernumber <> ?" if ($borrowernumber);
-  my $sth = $dbh->prepare($query);
-  if ($borrowernumber) {
-   $sth->execute($cardnumber,$borrowernumber);
-  } else { 
-     $sth->execute($cardnumber);
-  } 
-    if (my $data= $sth->fetchrow_hashref()){
-        return 1;
-    }
-    else {
-        return 0;
-    }
-}  
+    my $sth = $dbh->prepare($query);
+    $sth->execute(
+        $cardnumber,
+        ( $borrowernumber ? $borrowernumber : () )
+    );
+
+    return 1 if $sth->fetchrow_hashref;
+
+    my ( $min_length, $max_length ) = get_cardnumber_length();
+    return 2
+        if length $cardnumber > $max_length
+        or length $cardnumber < $min_length;
+
+    return 0;
+}
 
+=head2 get_cardnumber_length
+
+    my ($min, $max) = C4::Members::get_cardnumber_length()
+
+Returns the minimum and maximum length for patron cardnumbers as
+determined by the CardnumberLength system preference, the
+BorrowerMandatoryField system preference, and the width of the
+database column.
+
+=cut
+
+sub get_cardnumber_length {
+    my ( $min, $max ) = ( 0, 16 ); # borrowers.cardnumber is a nullable varchar(16)
+    $min = 1 if C4::Context->preference('BorrowerMandatoryField') =~ /cardnumber/;
+    if ( my $cardnumber_length = C4::Context->preference('CardnumberLength') ) {
+        # Is integer and length match
+        if ( $cardnumber_length =~ m|^\d+$| ) {
+            $min = $max = $cardnumber_length
+                if $cardnumber_length >= $min
+                    and $cardnumber_length <= $max;
+        }
+        # Else assuming it is a range
+        elsif ( $cardnumber_length =~ m|(\d*),(\d*)| ) {
+            $min = $1 if $1 and $min < $1;
+            $max = $2 if $2 and $max > $2;
+        }
+
+    }
+    return ( $min, $max );
+}
 
 =head2 getzipnamecity (OUEST-PROVENCE)
 
@@ -1552,6 +1683,7 @@ sub GetBorrowercategory {
     $categorycode = &GetBorrowerCategoryCode( $borrowernumber );
 
 Given the borrowernumber, the function returns the corresponding categorycode
+
 =cut
 
 sub GetBorrowerCategorycode {
@@ -1667,50 +1799,48 @@ sub GetAge{
     return $age;
 }    # sub get_age
 
-=head2 get_institutions
-
-  $insitutions = get_institutions();
-
-Just returns a list of all the borrowers of type I, borrownumber and name
-
-=cut
-
-#'
-sub get_institutions {
-    my $dbh = C4::Context->dbh();
-    my $sth =
-      $dbh->prepare(
-"SELECT borrowernumber,surname FROM borrowers WHERE categorycode=? ORDER BY surname"
-      );
-    $sth->execute('I');
-    my %orgs;
-    while ( my $data = $sth->fetchrow_hashref() ) {
-        $orgs{ $data->{'borrowernumber'} } = $data;
-    }
-    return ( \%orgs );
+=head2 SetAge
 
-}    # sub get_institutions
+  $borrower = C4::Members::SetAge($borrower, $datetimeduration);
+  $borrower = C4::Members::SetAge($borrower, '0015-12-10');
+  $borrower = C4::Members::SetAge($borrower, $datetimeduration, $datetime_reference);
 
-=head2 add_member_orgs
+  eval { $borrower = C4::Members::SetAge($borrower, '015-1-10'); };
+  if ($@) {print $@;} #Catch a bad ISO Date or kill your script!
 
-  add_member_orgs($borrowernumber,$borrowernumbers);
+This function sets the borrower's dateofbirth to match the given age.
+Optionally relative to the given $datetime_reference.
 
-Takes a borrowernumber and a list of other borrowernumbers and inserts them into the borrowers_to_borrowers table
+@PARAM1 koha.borrowers-object
+@PARAM2 DateTime::Duration-object as the desired age
+        OR a ISO 8601 Date. (To make the API more pleasant)
+@PARAM3 DateTime-object as the relative date, defaults to now().
+RETURNS The given borrower reference @PARAM1.
+DIES    If there was an error with the ISO Date handling.
 
 =cut
 
 #'
-sub add_member_orgs {
-    my ( $borrowernumber, $otherborrowers ) = @_;
-    my $dbh   = C4::Context->dbh();
-    my $query =
-      "INSERT INTO borrowers_to_borrowers (borrower1,borrower2) VALUES (?,?)";
-    my $sth = $dbh->prepare($query);
-    foreach my $otherborrowernumber (@$otherborrowers) {
-        $sth->execute( $borrowernumber, $otherborrowernumber );
+sub SetAge{
+    my ( $borrower, $datetimeduration, $datetime_ref ) = @_;
+    $datetime_ref = DateTime->now() unless $datetime_ref;
+
+    if ($datetimeduration && ref $datetimeduration ne 'DateTime::Duration') {
+        if ($datetimeduration =~ /^(\d{4})-(\d{2})-(\d{2})/) {
+            $datetimeduration = DateTime::Duration->new(years => $1, months => $2, days => $3);
+        }
+        else {
+            die "C4::Members::SetAge($borrower, $datetimeduration), datetimeduration not a valid ISO 8601 Date!\n";
+        }
     }
 
-}    # sub add_member_orgs
+    my $new_datetime_ref = $datetime_ref->clone();
+    $new_datetime_ref->subtract_duration( $datetimeduration );
+
+    $borrower->{dateofbirth} = $new_datetime_ref->ymd();
+
+    return $borrower;
+}    # sub SetAge
 
 =head2 GetCities
 
@@ -1771,27 +1901,22 @@ sub GetSortDetails {
   $result = &MoveMemberToDeleted($borrowernumber);
 
 Copy the record from borrowers to deletedborrowers table.
+The routine returns 1 for success, undef for failure.
 
 =cut
 
-# FIXME: should do it in one SQL statement w/ subquery
-# Otherwise, we should return the @data on success
-
 sub MoveMemberToDeleted {
     my ($member) = shift or return;
-    my $dbh = C4::Context->dbh;
-    my $query = qq|SELECT * 
-          FROM borrowers 
-          WHERE borrowernumber=?|;
-    my $sth = $dbh->prepare($query);
-    $sth->execute($member);
-    my @data = $sth->fetchrow_array;
-    (@data) or return;  # if we got a bad borrowernumber, there's nothing to insert
-    $sth =
-      $dbh->prepare( "INSERT INTO deletedborrowers VALUES ("
-          . ( "?," x ( scalar(@data) - 1 ) )
-          . "?)" );
-    $sth->execute(@data);
+
+    my $schema       = Koha::Database->new()->schema();
+    my $borrowers_rs = $schema->resultset('Borrower');
+    $borrowers_rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
+    my $borrower = $borrowers_rs->find($member);
+    return unless $borrower;
+
+    my $deleted = $schema->resultset('Deletedborrower')->create($borrower);
+
+    return $deleted ? 1 : undef;
 }
 
 =head2 DelMember
@@ -1857,48 +1982,6 @@ EOF
     return 0;
 }
 
-=head2 GetRoadTypes (OUEST-PROVENCE)
-
-  ($idroadtypearrayref, $roadttype_hashref) = &GetRoadTypes();
-
-Looks up the different road type . Returns two
-elements: a reference-to-array, which lists the id_roadtype
-codes, and a reference-to-hash, which maps the road type of the road .
-
-=cut
-
-sub GetRoadTypes {
-    my $dbh   = C4::Context->dbh;
-    my $query = qq|
-SELECT roadtypeid,road_type 
-FROM roadtype 
-ORDER BY road_type|;
-    my $sth = $dbh->prepare($query);
-    $sth->execute();
-    my %roadtype;
-    my @id;
-
-    #    insert empty value to create a empty choice in cgi popup
-
-    while ( my $data = $sth->fetchrow_hashref ) {
-
-        push @id, $data->{'roadtypeid'};
-        $roadtype{ $data->{'roadtypeid'} } = $data->{'road_type'};
-    }
-
-#test to know if the table contain some records if no the function return nothing
-    my $id = @id;
-    if ( $id eq 0 ) {
-        return ();
-    }
-    else {
-        unshift( @id, "" );
-        return ( \@id, \%roadtype );
-    }
-}
-
-
-
 =head2 GetTitles (OUEST-PROVENCE)
 
   ($borrowertitle)= &GetTitles();
@@ -1921,19 +2004,19 @@ sub GetTitles {
 
 =head2 GetPatronImage
 
-    my ($imagedata, $dberror) = GetPatronImage($cardnumber);
+    my ($imagedata, $dberror) = GetPatronImage($borrowernumber);
 
-Returns the mimetype and binary image data of the image for the patron with the supplied cardnumber.
+Returns the mimetype and binary image data of the image for the patron with the supplied borrowernumber.
 
 =cut
 
 sub GetPatronImage {
-    my ($cardnumber) = @_;
-    warn "Cardnumber passed to GetPatronImage is $cardnumber" if $debug;
+    my ($borrowernumber) = @_;
+    warn "Borrowernumber passed to GetPatronImage is $borrowernumber" if $debug;
     my $dbh = C4::Context->dbh;
-    my $query = 'SELECT mimetype, imagefile FROM patronimage WHERE cardnumber = ?';
+    my $query = 'SELECT mimetype, imagefile FROM patronimage WHERE borrowernumber = ?';
     my $sth = $dbh->prepare($query);
-    $sth->execute($cardnumber);
+    $sth->execute($borrowernumber);
     my $imagedata = $sth->fetchrow_hashref;
     warn "Database error!" if $sth->errstr;
     return $imagedata, $sth->errstr;
@@ -1952,7 +2035,7 @@ sub PutPatronImage {
     my ($cardnumber, $mimetype, $imgfile) = @_;
     warn "Parameters passed in: Cardnumber=$cardnumber, Mimetype=$mimetype, " . ($imgfile ? "Imagefile" : "No Imagefile") if $debug;
     my $dbh = C4::Context->dbh;
-    my $query = "INSERT INTO patronimage (cardnumber, mimetype, imagefile) VALUES (?,?,?) ON DUPLICATE KEY UPDATE imagefile = ?;";
+    my $query = "INSERT INTO patronimage (borrowernumber, mimetype, imagefile) VALUES ( ( SELECT borrowernumber from borrowers WHERE cardnumber = ? ),?,?) ON DUPLICATE KEY UPDATE imagefile = ?;";
     my $sth = $dbh->prepare($query);
     $sth->execute($cardnumber,$mimetype,$imgfile,$imgfile);
     warn "Error returned inserting $cardnumber.$mimetype." if $sth->errstr;
@@ -1961,19 +2044,19 @@ sub PutPatronImage {
 
 =head2 RmPatronImage
 
-    my ($dberror) = RmPatronImage($cardnumber);
+    my ($dberror) = RmPatronImage($borrowernumber);
 
-Removes the image for the patron with the supplied cardnumber.
+Removes the image for the patron with the supplied borrowernumber.
 
 =cut
 
 sub RmPatronImage {
-    my ($cardnumber) = @_;
-    warn "Cardnumber passed to GetPatronImage is $cardnumber" if $debug;
+    my ($borrowernumber) = @_;
+    warn "Borrowernumber passed to GetPatronImage is $borrowernumber" if $debug;
     my $dbh = C4::Context->dbh;
-    my $query = "DELETE FROM patronimage WHERE cardnumber = ?;";
+    my $query = "DELETE FROM patronimage WHERE borrowernumber = ?;";
     my $sth = $dbh->prepare($query);
-    $sth->execute($cardnumber);
+    $sth->execute($borrowernumber);
     my $dberror = $sth->errstr;
     warn "Database error!" if $sth->errstr;
     return $dberror;
@@ -1998,29 +2081,6 @@ sub GetHideLostItemsPreference {
     return $hidelostitems;    
 }
 
-=head2 GetRoadTypeDetails (OUEST-PROVENCE)
-
-  ($roadtype) = &GetRoadTypeDetails($roadtypeid);
-
-Returns the description of roadtype
-C<&$roadtype>return description of road type
-C<&$roadtypeid>this is the value of roadtype s
-
-=cut
-
-sub GetRoadTypeDetails {
-    my ($roadtypeid) = @_;
-    my $dbh          = C4::Context->dbh;
-    my $query        = qq|
-SELECT road_type 
-FROM roadtype 
-WHERE roadtypeid=?|;
-    my $sth = $dbh->prepare($query);
-    $sth->execute($roadtypeid);
-    my $roadtype = $sth->fetchrow;
-    return ($roadtype);
-}
-
 =head2 GetBorrowersToExpunge
 
   $borrowers = &GetBorrowersToExpunge(
@@ -2043,7 +2103,7 @@ sub GetBorrowersToExpunge {
     my $filterbranch   = $params->{'branchcode'} ||
                         ((C4::Context->preference('IndependentBranches')
                              && C4::Context->userenv 
-                             && C4::Context->userenv->{flags} % 2 !=1 
+                             && !C4::Context->IsSuperLibrarian()
                              && C4::Context->userenv->{branch})
                          ? C4::Context->userenv->{branch}
                          : "");  
@@ -2109,7 +2169,7 @@ sub GetBorrowersWhoHaveNeverBorrowed {
     my $filterbranch = shift || 
                         ((C4::Context->preference('IndependentBranches')
                              && C4::Context->userenv 
-                             && C4::Context->userenv->{flags} % 2 !=1 
+                             && !C4::Context->IsSuperLibrarian()
                              && C4::Context->userenv->{branch})
                          ? C4::Context->userenv->{branch}
                          : "");  
@@ -2159,7 +2219,7 @@ sub GetBorrowersWithIssuesHistoryOlderThan {
     my $filterbranch = shift || 
                         ((C4::Context->preference('IndependentBranches')
                              && C4::Context->userenv 
-                             && C4::Context->userenv->{flags} % 2 !=1 
+                             && !C4::Context->IsSuperLibrarian()
                              && C4::Context->userenv->{branch})
                          ? C4::Context->userenv->{branch}
                          : "");  
@@ -2213,45 +2273,15 @@ sub GetBorrowersNamesAndLatestIssue {
     return $results;
 }
 
-=head2 DebarMember
-
-my $success = DebarMember( $borrowernumber, $todate );
-
-marks a Member as debarred, and therefore unable to checkout any more
-items.
-
-return :
-true on success, false on failure
-
-=cut
-
-sub DebarMember {
-    my $borrowernumber = shift;
-    my $todate         = shift;
-
-    return unless defined $borrowernumber;
-    return unless $borrowernumber =~ /^\d+$/;
-
-    return ModMember(
-        borrowernumber => $borrowernumber,
-        debarred       => $todate
-    );
-
-}
-
 =head2 ModPrivacy
 
-=over 4
-
-my $success = ModPrivacy( $borrowernumber, $privacy );
+  my $success = ModPrivacy( $borrowernumber, $privacy );
 
 Update the privacy of a patron.
 
 return :
 true on success, false on failure
 
-=back
-
 =cut
 
 sub ModPrivacy {
@@ -2448,7 +2478,7 @@ sub IssueSlip {
             'news' => [ map {
                 $_->{'timestamp'} = $_->{'newdate'};
                 { opac_news => $_ }
-            } @{ GetNewsToDisplay("slip") } ],
+            } @{ GetNewsToDisplay("slip",$branch) } ],
         );
     }
 
@@ -2537,6 +2567,17 @@ sub AddEnrolmentFeeIfNeeded {
     }
 }
 
+sub HasOverdues {
+    my ( $borrowernumber ) = @_;
+
+    my $sql = "SELECT COUNT(*) FROM issues WHERE date_due < NOW() AND borrowernumber = ?";
+    my $sth = C4::Context->dbh->prepare( $sql );
+    $sth->execute( $borrowernumber );
+    my ( $count ) = $sth->fetchrow_array();
+
+    return $count;
+}
+
 END { }    # module clean-up code here (global destructor)
 
 1;