Bug 32030: ERM - related agreement koha classes
[srvgit] / Koha / Patron.pm
index c424c30..96a72a3 100644 (file)
@@ -26,18 +26,21 @@ use Unicode::Normalize qw( NFKD );
 use Try::Tiny;
 
 use C4::Context;
+use C4::Auth qw( checkpw_hash );
 use C4::Log qw( logaction );
 use Koha::Account;
 use Koha::ArticleRequests;
-use C4::Letters;
+use C4::Letters qw( GetPreparedLetter EnqueueLetter SendQueuedMessages );
 use Koha::AuthUtils;
 use Koha::Checkouts;
 use Koha::CirculationRules;
 use Koha::Club::Enrollments;
 use Koha::Database;
 use Koha::DateUtils qw( dt_from_string );
+use Koha::Encryption;
 use Koha::Exceptions::Password;
 use Koha::Holds;
+use Koha::CurbsidePickups;
 use Koha::Old::Checkouts;
 use Koha::Patron::Attributes;
 use Koha::Patron::Categories;
@@ -267,6 +270,9 @@ sub store {
                 # Make a copy of the plain text password for later use
                 $self->plain_text_password( $self->password );
 
+                $self->password_expiration_date( $self->password
+                    ? $self->category->get_password_expiry_date || undef
+                    : undef );
                 # Create a disabled account if no password provided
                 $self->password( $self->password
                     ? Koha::AuthUtils::hash_password( $self->password )
@@ -303,8 +309,7 @@ sub store {
 
                     # Clean up guarantors on category change if required
                     $self->guarantor_relationships->delete
-                      if ( $self->category->category_type ne 'C'
-                        && $self->category->category_type ne 'P' );
+                      unless ( $self->category->can_be_guarantee );
 
                 }
 
@@ -365,8 +370,8 @@ $patron->delete
 
 Delete patron's holds, lists and finally the patron.
 
-Lists owned by the borrower are deleted, but entries from the borrower to
-other lists are kept.
+Lists owned by the borrower are deleted or ownership is transferred depending on the
+ListOwnershipUponPatronDeletion pref, but entries from the borrower to other lists are kept.
 
 =cut
 
@@ -384,21 +389,8 @@ sub delete {
                 $hold->cancel;
             }
 
-            # Delete all lists and all shares of this borrower
-            # Consistent with the approach Koha uses on deleting individual lists
-            # Note that entries in virtualshelfcontents added by this borrower to
-            # lists of others will be handled by a table constraint: the borrower
-            # is set to NULL in those entries.
-            # NOTE:
-            # We could handle the above deletes via a constraint too.
-            # But a new BZ report 11889 has been opened to discuss another approach.
-            # Instead of deleting we could also disown lists (based on a pref).
-            # In that way we could save shared and public lists.
-            # The current table constraints support that idea now.
-            # This pref should then govern the results of other routines/methods such as
-            # Koha::Virtualshelf->new->delete too.
-            # FIXME Could be $patron->get_lists
-            $_->delete for Koha::Virtualshelves->search( { owner => $self->borrowernumber } )->as_list;
+            # Handle lists (virtualshelves)
+            $self->virtualshelves->disown_or_delete;
 
             # We cannot have a FK on borrower_modifications.borrowernumber, the table is also used
             # for patron selfreg
@@ -412,7 +404,6 @@ sub delete {
     return $self;
 }
 
-
 =head3 category
 
 my $patron_category = $patron->category
@@ -787,6 +778,21 @@ sub is_expired {
     return 0;
 }
 
+=head3 password_expired
+
+my $password_expired = $patron->password_expired;
+
+Returns 1 if the patron's password is expired or 0;
+
+=cut
+
+sub password_expired {
+    my ($self) = @_;
+    return 0 unless $self->password_expiration_date;
+    return 1 if dt_from_string( $self->password_expiration_date ) <= dt_from_string->truncate( to => 'day' );
+    return 0;
+}
+
 =head3 is_going_to_expire
 
 my $is_going_to_expire = $patron->is_going_to_expire;
@@ -884,8 +890,43 @@ sub set_password {
         }
     }
 
+    if ( C4::Context->preference('NotifyPasswordChange') ) {
+        my $self_from_storage = $self->get_from_storage;
+        if ( !C4::Auth::checkpw_hash( $password, $self_from_storage->password ) ) {
+            my $emailaddr = $self_from_storage->notice_email_address;
+
+            # if we manage to find a valid email address, send notice
+            if ($emailaddr) {
+                my $letter = C4::Letters::GetPreparedLetter(
+                    module      => 'members',
+                    letter_code => 'PASSWORD_CHANGE',
+                    branchcode  => $self_from_storage->branchcode,
+                    ,
+                    lang   => $self_from_storage->lang || 'default',
+                    tables => {
+                        'branches'  => $self_from_storage->branchcode,
+                        'borrowers' => $self_from_storage->borrowernumber,
+                    },
+                    want_librarian => 1,
+                ) or return;
+
+                my $message_id = C4::Letters::EnqueueLetter(
+                    {
+                        letter                 => $letter,
+                        borrowernumber         => $self_from_storage->id,
+                        to_address             => $emailaddr,
+                        message_transport_type => 'email'
+                    }
+                );
+                C4::Letters::SendQueuedMessages( { message_id => $message_id } );
+            }
+        }
+    }
+
     my $digest = Koha::AuthUtils::hash_password($password);
 
+    $self->password_expiration_date( $self->category->get_password_expiry_date || undef );
+
     # We do not want to call $self->store and retrieve password from DB
     $self->password($digest);
     $self->login_attempts(0);
@@ -1194,15 +1235,15 @@ sub old_checkouts {
     return Koha::Old::Checkouts->_new_from_dbic( $old_checkouts );
 }
 
-=head3 get_overdues
+=head3 overdues
 
-my $overdue_items = $patron->get_overdues
+my $overdue_items = $patron->overdues
 
 Return the overdue items
 
 =cut
 
-sub get_overdues {
+sub overdues {
     my ($self) = @_;
     my $dtf = Koha::Database->new->schema->storage->datetime_parser;
     return $self->checkouts->search(
@@ -1215,8 +1256,6 @@ sub get_overdues {
     );
 }
 
-sub overdues { my $self = shift; return $self->get_overdues(@_); }
-
 =head3 get_routing_lists
 
 my $routinglists = $patron->get_routing_lists
@@ -1307,6 +1346,20 @@ sub old_holds {
     return Koha::Old::Holds->_new_from_dbic($old_holds_rs);
 }
 
+=head3 curbside_pickups
+
+my $curbside_pickups = $patron->curbside_pickups;
+
+Return all the curbside pickups for this patron
+
+=cut
+
+sub curbside_pickups {
+    my ($self) = @_;
+    my $curbside_pickups_rs = $self->_result->curbside_pickups_borrowernumbers->search;
+    return Koha::CurbsidePickups->_new_from_dbic($curbside_pickups_rs);
+}
+
 =head3 return_claims
 
 my $return_claims = $patron->return_claims
@@ -1689,8 +1742,9 @@ sub extended_attributes {
                     Koha::Patron::Attribute::Types->search(
                         {
                             mandatory => 1,
+                            category_code => [ undef, $self->categorycode ],
                             'borrower_attribute_types_branches.b_branchcode' =>
-                              undef
+                              undef,
                         },
                         { join => 'borrower_attribute_types_branches' }
                     )->get_column('code');
@@ -1896,7 +1950,6 @@ sub to_api_mapping {
         emailpro            => 'secondary_email',
         flags               => undef,    # permissions manipulation handled in /permissions
         gonenoaddress       => 'incorrect_address',
-        guarantorid         => 'guarantor_id',
         lastseen            => 'last_seen',
         lost                => 'patron_card_lost',
         opacnote            => 'opac_notes',
@@ -1931,6 +1984,7 @@ sub to_api_mapping {
         altcontactsurname   => 'altcontact_surname',
         altcontactstate     => 'altcontact_state',
         altcontactzipcode   => 'altcontact_postal_code',
+        password_expiration_date => undef,
         primary_contact_method => undef,
         secret              => undef,
         auth_method         => undef,
@@ -2072,7 +2126,7 @@ Return the patron's recalls.
 sub recalls {
     my ( $self ) = @_;
 
-    return Koha::Recalls->search({ borrowernumber => $self->borrowernumber });
+    return Koha::Recalls->search({ patron_id => $self->borrowernumber });
 }
 
 =head3 account_balance
@@ -2088,6 +2142,57 @@ sub account_balance {
     return $self->account->balance;
 }
 
+=head3 notify_library_of_registration
+
+$patron->notify_library_of_registration( $email_patron_registrations );
+
+Send patron registration email to library if EmailPatronRegistrations system preference is enabled.
+
+=cut
+
+sub notify_library_of_registration {
+    my ( $self, $email_patron_registrations ) = @_;
+
+    if (
+        my $letter = C4::Letters::GetPreparedLetter(
+            module      => 'members',
+            letter_code => 'OPAC_REG',
+            branchcode  => $self->branchcode,
+            lang        => $self->lang || 'default',
+            tables      => {
+                'borrowers' => $self->borrowernumber
+            },
+        )
+    ) {
+        my $to_address;
+        if ( $email_patron_registrations eq "BranchEmailAddress" ) {
+            my $library = Koha::Libraries->find( $self->branchcode );
+            $to_address = $library->inbound_email_address;
+        }
+        elsif ( $email_patron_registrations eq "KohaAdminEmailAddress" ) {
+            $to_address = C4::Context->preference('ReplytoDefault')
+            || C4::Context->preference('KohaAdminEmailAddress');
+        }
+        else {
+            $to_address =
+                C4::Context->preference('EmailAddressForPatronRegistrations')
+                || C4::Context->preference('ReplytoDefault')
+                || C4::Context->preference('KohaAdminEmailAddress');
+        }
+
+        my $message_id = C4::Letters::EnqueueLetter(
+            {
+                letter                 => $letter,
+                borrowernumber         => $self->borrowernumber,
+                to_address             => $to_address,
+                message_transport_type => 'email'
+            }
+        ) or warn "can't enqueue letter $letter";
+        if ( $message_id ) {
+            return 1;
+        }
+    }
+}
 
 =head3 has_messaging_preference
 
@@ -2131,6 +2236,51 @@ sub can_patron_change_staff_only_lists {
     return 0;
 }
 
+=head3 encode_secret
+
+  $patron->encode_secret($secret32);
+
+Secret (TwoFactorAuth expects it in base32 format) is encrypted.
+You still need to call ->store.
+
+=cut
+
+sub encode_secret {
+    my ( $self, $secret ) = @_;
+    if( $secret ) {
+        return $self->secret( Koha::Encryption->new->encrypt_hex($secret) );
+    }
+    return $self->secret($secret);
+}
+
+=head3 decoded_secret
+
+  my $secret32 = $patron->decoded_secret;
+
+Decode the patron secret. We expect to get back a base32 string, but this
+is not checked here. Caller of encode_secret is responsible for that.
+
+=cut
+
+sub decoded_secret {
+    my ( $self ) = @_;
+    if( $self->secret ) {
+        return Koha::Encryption->new->decrypt_hex( $self->secret );
+    }
+    return $self->secret;
+}
+
+=head3 virtualshelves
+
+    my $shelves = $patron->virtualshelves;
+
+=cut
+
+sub virtualshelves {
+    my $self = shift;
+    return Koha::Virtualshelves->_new_from_dbic( scalar $self->_result->virtualshelves );
+}
+
 =head2 Internal methods
 
 =head3 _type