use Carp;
use List::MoreUtils qw( uniq );
+use Module::Load::Conditional qw( can_load );
use Text::Unaccent qw( unac_string );
use C4::Context;
use Koha::Virtualshelves;
use Koha::Club::Enrollments;
use Koha::Account;
+use Koha::Subscription::Routinglists;
+
+if ( ! can_load( modules => { 'Koha::NorwegianPatronDB' => undef } ) ) {
+ warn "Unable to load Koha::NorwegianPatronDB";
+}
use base qw(Koha::Object);
+our $RESULTSET_PATRON_ID_MAPPING = {
+ Accountline => 'borrowernumber',
+ Aqbasketuser => 'borrowernumber',
+ Aqbudget => 'budget_owner_id',
+ Aqbudgetborrower => 'borrowernumber',
+ ArticleRequest => 'borrowernumber',
+ BorrowerAttribute => 'borrowernumber',
+ BorrowerDebarment => 'borrowernumber',
+ BorrowerFile => 'borrowernumber',
+ BorrowerModification => 'borrowernumber',
+ ClubEnrollment => 'borrowernumber',
+ Issue => 'borrowernumber',
+ ItemsLastBorrower => 'borrowernumber',
+ Linktracker => 'borrowernumber',
+ Message => 'borrowernumber',
+ MessageQueue => 'borrowernumber',
+ OldIssue => 'borrowernumber',
+ OldReserve => 'borrowernumber',
+ Rating => 'borrowernumber',
+ Reserve => 'borrowernumber',
+ Review => 'borrowernumber',
+ SearchHistory => 'userid',
+ Statistic => 'borrowernumber',
+ Suggestion => 'suggestedby',
+ TagAll => 'borrowernumber',
+ Virtualshelfcontent => 'borrowernumber',
+ Virtualshelfshare => 'borrowernumber',
+ Virtualshelve => 'owner',
+};
+
=head1 NAME
Koha::Patron - Koha Patron Object class
=cut
+=head3 new
+
+=cut
+
+sub new {
+ my ( $class, $params ) = @_;
+
+ return $class->SUPER::new($params);
+}
+
+sub fixup_cardnumber {
+ my ( $self ) = @_;
+ my $max = Koha::Patrons->search({
+ cardnumber => {-regexp => '^-?[0-9]+$'}
+ }, {
+ select => \'CAST(cardnumber AS SIGNED)',
+ as => ['cast_cardnumber']
+ })->_resultset->get_column('cast_cardnumber')->max;
+ $self->cardnumber(($max || 0) +1);
+}
+
+# trim whitespace from data which has some non-whitespace in it.
+# Could be moved to Koha::Object if need to be reused
+sub trim_whitespaces {
+ my( $self ) = @_;
+
+ my $schema = Koha::Database->new->schema;
+ my @columns = $schema->source($self->_type)->columns;
+
+ for my $column( @columns ) {
+ my $value = $self->$column;
+ if ( defined $value ) {
+ $value =~ s/^\s*|\s*$//g;
+ $self->$column($value);
+ }
+ }
+ return $self;
+}
+
+sub plain_text_password {
+ my ( $self, $password ) = @_;
+ if ( $password ) {
+ $self->{_plain_text_password} = $password;
+ return $self;
+ }
+ return $self->{_plain_text_password}
+ if $self->{_plain_text_password};
+
+ return;
+}
+
+sub store {
+ my ($self) = @_;
+
+ $self->_result->result_source->schema->txn_do(
+ sub {
+ if (
+ C4::Context->preference("autoMemberNum")
+ and ( not defined $self->cardnumber
+ or $self->cardnumber eq '' )
+ )
+ {
+ # Warning: The caller is responsible for locking the members table in write
+ # mode, to avoid database corruption.
+ # We are in a transaction but the table is not locked
+ $self->fixup_cardnumber;
+ }
+ unless ( $self->in_storage ) { #AddMember
+
+ unless( $self->category->in_storage ) {
+ Koha::Exceptions::Object::FKConstraint->throw(
+ broken_fk => 'categorycode',
+ value => $self->categorycode,
+ );
+ }
+
+ $self->trim_whitespaces;
+
+ # Generate a valid userid/login if needed
+ $self->userid($self->generate_userid)
+ if not $self->userid or not $self->has_valid_userid;
+
+ # Add expiration date if it isn't already there
+ unless ( $self->dateexpiry ) {
+ $self->dateexpiry( $self->category->get_expiry_date );
+ }
+
+ # Add enrollment date if it isn't already there
+ unless ( $self->dateenrolled ) {
+ $self->dateenrolled(dt_from_string);
+ }
+
+ # Set the privacy depending on the patron's category
+ my $default_privacy = $self->category->default_privacy || q{};
+ $default_privacy =
+ $default_privacy eq 'default' ? 1
+ : $default_privacy eq 'never' ? 2
+ : $default_privacy eq 'forever' ? 0
+ : undef;
+ $self->privacy($default_privacy);
+
+ unless ( defined $self->privacy_guarantor_checkouts ) {
+ $self->privacy_guarantor_checkouts(0);
+ }
+
+ # Make a copy of the plain text password for later use
+ $self->plain_text_password( $self->password );
+
+ # Create a disabled account if no password provided
+ $self->password( $self->password
+ ? Koha::AuthUtils::hash_password( $self->password )
+ : '!' );
+
+ # We don't want invalid dates in the db (mysql has a bad habit of inserting 0000-00-00)
+ $self->dateofbirth(undef) unless $self->dateofbirth;
+ $self->debarred(undef) unless $self->debarred;
+
+ # Set default values if not set
+ $self->sms_provider_id(undef) unless $self->sms_provider_id;
+ $self->guarantorid(undef) unless $self->guarantorid;
+
+ $self->borrowernumber(undef);
+
+ $self = $self->SUPER::store;
+
+ # 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 )
+ {
+ Koha::Database->new->schema->resultset('BorrowerSync')
+ ->create(
+ {
+ 'borrowernumber' => $self->borrowernumber,
+ 'synctype' => 'norwegianpatrondb',
+ 'sync' => 1,
+ 'syncstatus' => 'new',
+ 'hashed_pin' =>
+ Koha::NorwegianPatronDB::NLEncryptPIN($self->plain_text_password),
+ }
+ );
+ }
+
+ $self->add_enrolment_fee_if_needed;
+
+ logaction( "MEMBERS", "CREATE", $self->borrowernumber, "" )
+ if C4::Context->preference("BorrowersLog");
+ }
+ else { #ModMember
+ $self = $self->SUPER::store;
+ }
+
+ }
+ );
+ return $self;
+}
+
=head3 delete
$patron->delete
);
}
+=head3 merge_with
+
+ my $patron = Koha::Patrons->find($id);
+ $patron->merge_with( \@patron_ids );
+
+ This subroutine merges a list of patrons into the patron record. This is accomplished by finding
+ all related patron ids for the patrons to be merged in other tables and changing the ids to be that
+ of the keeper patron.
+
+=cut
+
+sub merge_with {
+ my ( $self, $patron_ids ) = @_;
+
+ my @patron_ids = @{ $patron_ids };
+
+ # Ensure the keeper isn't in the list of patrons to merge
+ @patron_ids = grep { $_ ne $self->id } @patron_ids;
+
+ my $schema = Koha::Database->new()->schema();
+
+ my $results;
+
+ $self->_result->result_source->schema->txn_do( sub {
+ foreach my $patron_id (@patron_ids) {
+ my $patron = Koha::Patrons->find( $patron_id );
+
+ next unless $patron;
+
+ # Unbless for safety, the patron will end up being deleted
+ $results->{merged}->{$patron_id}->{patron} = $patron->unblessed;
+
+ while (my ($r, $field) = each(%$RESULTSET_PATRON_ID_MAPPING)) {
+ my $rs = $schema->resultset($r)->search({ $field => $patron_id });
+ $results->{merged}->{ $patron_id }->{updated}->{$r} = $rs->count();
+ $rs->update({ $field => $self->id });
+ }
+
+ $patron->move_to_deleted();
+ $patron->delete();
+ }
+ });
+
+ return $results;
+}
+
+
+
=head3 wants_check_for_previous_checkout
$wants_check = $patron->wants_check_for_previous_checkout;
my $overdue_items = $patron->get_overdues
-Return the overdued items
+Return the overdue items
=cut
);
}
+=head3 get_routing_lists
+
+my @routinglists = $patron->get_routing_lists
+
+Returns the routing lists a patron is subscribed to.
+
+=cut
+
+sub get_routing_lists {
+ my ($self) = @_;
+ my $routing_list_rs = $self->_result->subscriptionroutinglists;
+ return Koha::Subscription::Routinglists->_new_from_dbic($routing_list_rs);
+}
+
=head3 get_age
my $age = $patron->get_age
my ($self) = @_;
my $userid;
my $offset = 0;
- my $patron = Koha::Patron->new;
+ my $existing_userid = $self->userid;
my $firstname = $self->firstname // q{};
my $surname = $self->surname // q{};
#The script will "do" the following code and increment the $offset until the generated userid is unique
$userid = lc(($firstname)? "$firstname.$surname" : $surname);
$userid = unac_string('utf-8',$userid);
$userid .= $offset unless $offset == 0;
- $patron->userid( $userid );
+ $self->userid( $userid );
$offset++;
- } while (! $patron->has_valid_userid );
+ } while (! $self->has_valid_userid );
+
+ # Resetting to the previous value as the callers do not expect
+ # this method to modify the userid attribute
+ # This will be done later (move of AddMember and ModMember)
+ $self->userid( $existing_userid );
return $userid;