+=head3 queue_notice
+
+ Koha::Patrons->queue_notice({ letter_params => $letter_params, message_name => 'DUE'});
+ Koha::Patrons->queue_notice({ letter_params => $letter_params, message_transports => \@message_transports });
+ Koha::Patrons->queue_notice({ letter_params => $letter_params, message_transports => \@message_transports, test_mode => 1 });
+
+ Queue messages to a patron. Can pass a message that is part of the message_attributes
+ table or supply the transport to use.
+
+ If passed a message name we retrieve the patrons preferences for transports
+ Otherwise we use the supplied transport. In the case of email or sms we fall back to print if
+ we have no address/number for sending
+
+ $letter_params is a hashref of the values to be passed to GetPreparedLetter
+
+ test_mode will only report which notices would be sent, but nothing will be queued
+
+=cut
+
+sub queue_notice {
+ my ( $self, $params ) = @_;
+ my $letter_params = $params->{letter_params};
+ my $test_mode = $params->{test_mode};
+
+ return unless $letter_params;
+ return unless exists $params->{message_name} xor $params->{message_transports}; # We only want one of these
+
+ my $library = Koha::Libraries->find( $letter_params->{branchcode} );
+ my $from_email_address = $library->from_email_address;
+
+ my @message_transports;
+ my $letter_code;
+ $letter_code = $letter_params->{letter_code};
+ if( $params->{message_name} ){
+ my $messaging_prefs = C4::Members::Messaging::GetMessagingPreferences( {
+ borrowernumber => $letter_params->{borrowernumber},
+ message_name => $params->{message_name}
+ } );
+ @message_transports = ( keys %{ $messaging_prefs->{transports} } );
+ $letter_code = $messaging_prefs->{transports}->{$message_transports[0]} unless $letter_code;
+ } else {
+ @message_transports = @{$params->{message_transports}};
+ }
+ return unless defined $letter_code;
+ $letter_params->{letter_code} = $letter_code;
+ my $print_sent = 0;
+ my %return;
+ foreach my $mtt (@message_transports){
+ next if ($mtt eq 'itiva' and C4::Context->preference('TalkingTechItivaPhoneNotification') );
+ # Notice is handled by TalkingTech_itiva_outbound.pl
+ if ( ( $mtt eq 'email' and not $self->notice_email_address )
+ or ( $mtt eq 'sms' and not $self->smsalertnumber )
+ or ( $mtt eq 'phone' and not $self->phone ) )
+ {
+ push @{ $return{fallback} }, $mtt;
+ $mtt = 'print';
+ }
+ next if $mtt eq 'print' && $print_sent;
+ $letter_params->{message_transport_type} = $mtt;
+ my $letter = C4::Letters::GetPreparedLetter( %$letter_params );
+ C4::Letters::EnqueueLetter({
+ letter => $letter,
+ borrowernumber => $self->borrowernumber,
+ from_address => $from_email_address,
+ message_transport_type => $mtt
+ }) unless $test_mode;
+ push @{$return{sent}}, $mtt;
+ $print_sent = 1 if $mtt eq 'print';
+ }
+ return \%return;
+}
+
+=head3 safe_to_delete
+
+ my $result = $patron->safe_to_delete;
+ if ( $result eq 'has_guarantees' ) { ... }
+ elsif ( $result ) { ... }
+ else { # cannot delete }
+
+This method tells if the Koha:Patron object can be deleted. Possible return values
+
+=over 4
+
+=item 'ok'
+
+=item 'has_checkouts'
+
+=item 'has_debt'
+
+=item 'has_guarantees'
+
+=item 'is_anonymous_patron'
+
+=back
+
+=cut
+
+sub safe_to_delete {
+ my ($self) = @_;
+
+ my $anonymous_patron = C4::Context->preference('AnonymousPatron');
+
+ my $error;
+
+ if ( $anonymous_patron && $self->id eq $anonymous_patron ) {
+ $error = 'is_anonymous_patron';
+ }
+ elsif ( $self->checkouts->count ) {
+ $error = 'has_checkouts';
+ }
+ elsif ( $self->account->outstanding_debits->total_outstanding > 0 ) {
+ $error = 'has_debt';
+ }
+ elsif ( $self->guarantee_relationships->count ) {
+ $error = 'has_guarantees';
+ }
+
+ if ( $error ) {
+ return Koha::Result::Boolean->new(0)->add_message({ message => $error });
+ }
+
+ return Koha::Result::Boolean->new(1);
+}
+
+=head3 recalls
+
+ my $recalls = $patron->recalls;
+
+Return the patron's recalls.
+
+=cut
+
+sub recalls {
+ my ( $self ) = @_;
+
+ return Koha::Recalls->search({ patron_id => $self->borrowernumber });
+}
+
+=head3 account_balance
+
+ my $balance = $patron->account_balance
+
+Return the patron's account balance
+
+=cut
+
+sub account_balance {
+ my ($self) = @_;
+ 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
+
+my $bool = $patron->has_messaging_preference({
+ message_name => $message_name, # A value from message_attributes.message_name
+ message_transport_type => $message_transport_type, # email, sms, phone, itiva, etc...
+ wants_digest => $wants_digest, # 1 if you are looking for the digest version, don't pass if you just want either
+});
+
+=cut
+
+sub has_messaging_preference {
+ my ( $self, $params ) = @_;
+
+ my $message_name = $params->{message_name};
+ my $message_transport_type = $params->{message_transport_type};
+ my $wants_digest = $params->{wants_digest};
+
+ return $self->_result->search_related_rs(
+ 'borrower_message_preferences',
+ $params,
+ {
+ prefetch =>
+ [ 'borrower_message_transport_preferences', 'message_attribute' ]
+ }
+ )->count;
+}
+
+=head3 can_patron_change_staff_only_lists
+
+$patron->can_patron_change_staff_only_lists;
+
+Return 1 if a patron has 'Superlibrarian' or 'Catalogue' permission.
+Otherwise, return 0.
+
+=cut
+
+sub can_patron_change_staff_only_lists {
+ my ( $self, $params ) = @_;
+ return 1 if C4::Auth::haspermission( $self->userid, { 'catalogue' => 1 });
+ 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 );
+}
+