#use warnings; FIXME - Bug 2505
use C4::Context;
use C4::Dates qw(format_date_in_iso format_date);
-use Digest::MD5 qw(md5_base64);
use String::Random qw( random_string );
use Date::Calc qw/Today Add_Delta_YM check_date Date_to_Days/;
use C4::Log; # logaction
use DateTime;
use DateTime::Format::DateParse;
use Koha::DateUtils;
+use Koha::Borrower::Debarments qw(IsDebarred);
use Text::Unaccent qw( unac_string );
+use Koha::AuthUtils qw(hash_password);
our ($VERSION,@ISA,@EXPORT,@EXPORT_OK,$debug);
&getidcity
&GetFirstValidEmailAddress
+ &GetNoticeEmailAddress
&GetAge
&GetCities
&IssueSlip
GetBorrowersWithEmail
+
+ HasOverdues
);
#Modify data
$filter = [ $filter ];
push @$filter, {"borrowernumber"=>$matching_records};
}
- }
+ }
}
# $showallbranches was not used at the time SearchMember() was mainstreamed into Search().
# Mentioning for the reference
- if ( C4::Context->preference("IndependantBranches") ) { # && !$showallbranches){
+ if ( C4::Context->preference("IndependentBranches") ) { # && !$showallbranches){
if ( my $userenv = C4::Context->userenv ) {
my $branch = $userenv->{'branch'};
- if ( ($userenv->{flags} % 2 !=1) &&
- $branch && $branch ne "insecure" ){
-
+ if ( !C4::Context->IsSuperLibrarian() && $branch ){
if (my $fr = ref $filter) {
if ( $fr eq "HASH" ) {
$filter->{branchcode} = $branch;
}
$searchtype ||= "start_with";
- return SearchInTable( "borrowers", $filter, $orderby, $limit, $columns_out, $search_on_fields, $searchtype );
+ return SearchInTable( "borrowers", $filter, $orderby, $limit, $columns_out, $search_on_fields, $searchtype );
}
=head2 GetMemberDetails
if ( $owing > 0 ) {
my %flaginfo;
my $noissuescharge = C4::Context->preference("noissuescharge") || 5;
- $flaginfo{'message'} = sprintf "Patron owes \$%.02f", $owing;
+ $flaginfo{'message'} = sprintf 'Patron owes %.02f', $owing;
$flaginfo{'amount'} = sprintf "%.02f", $owing;
if ( $owing > $noissuescharge && !C4::Context->preference("AllowFineOverride") ) {
$flaginfo{'noissues'} = 1;
}
elsif ( $balance < 0 ) {
my %flaginfo;
- $flaginfo{'message'} = sprintf "Patron has credit of \$%.02f", -$balance;
+ $flaginfo{'message'} = sprintf 'Patron has credit of %.02f', -$balance;
$flaginfo{'amount'} = sprintf "%.02f", $balance;
$flags{'CREDITS'} = \%flaginfo;
}
my $borrowernumber = shift;
my $dbh = C4::Context->dbh;
- my $blockeddate = CheckBorrowerDebarred($borrowernumber);
+ my $blockeddate = Koha::Borrower::Debarments::IsDebarred($borrowernumber);
return ( 1, $blockeddate ) if $blockeddate;
return ($overdue_count, $issue_count, $total_fines);
}
-sub columns(;$) {
- return @{C4::Context->dbh->selectcol_arrayref("SHOW columns from borrowers")};
+
+=head2 columns
+
+ my @columns = C4::Member::columns();
+
+Returns an array of borrowers' table columns on success,
+and an empty array on failure.
+
+=cut
+
+sub columns {
+
+ # Pure ANSI SQL goodness.
+ my $sql = 'SELECT * FROM borrowers WHERE 1=0;';
+
+ # Get the database handle.
+ my $dbh = C4::Context->dbh;
+
+ # Run the SQL statement to load STH's readonly properties.
+ my $sth = $dbh->prepare($sql);
+ my $rv = $sth->execute();
+
+ # This only fails if the table doesn't exist.
+ # This will always be called AFTER an install or upgrade,
+ # so borrowers will exist!
+ my @data;
+ if ($sth->{NUM_OF_FIELDS}>0) {
+ @data = @{$sth->{NAME}};
+ }
+ else {
+ @data = ();
+ }
+ return @data;
}
+
=head2 ModMember
my $success = ModMember(borrowernumber => $borrowernumber,
if ($data{password} eq '****' or $data{password} eq '') {
delete $data{password};
} else {
- $data{password} = md5_base64($data{password});
+ $data{password} = hash_password($data{password});
}
}
- my $execute_success=UpdateInTable("borrowers",\%data);
+ my $old_categorycode = GetBorrowerCategorycode( $data{borrowernumber} );
+ my $execute_success=UpdateInTable("borrowers",\%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
# is adult check guarantees;
UpdateGuarantees(%data);
}
+
+ # If the patron changes to a category with enrollment fee, we add a fee
+ if ( $data{categorycode} and $data{categorycode} ne $old_categorycode ) {
+ AddEnrolmentFeeIfNeeded( $data{categorycode}, $data{borrowernumber} );
+ }
+
logaction("MEMBERS", "MODIFY", $data{'borrowernumber'}, "UPDATE (executed w/ arg: $data{'borrowernumber'})") if C4::Context->preference("BorrowersLog");
}
return $execute_success;
}
-
=head2 AddMember
$borrowernumber = &AddMember(%borrower);
}
# create a disabled account if no password provided
- $data{'password'} = ($data{'password'})? md5_base64($data{'password'}) : '!';
+ $data{'password'} = ($data{'password'})? hash_password($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");
-
- # check for enrollment fee & add it if needed
- 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);
- }
+
+ AddEnrolmentFeeIfNeeded( $data{categorycode}, $data{borrowernumber} );
return $data{'borrowernumber'};
}
+=head2 Check_Userid
+
+ my $uniqueness = Check_Userid($userid,$borrowernumber);
+
+ $borrowernumber is optional (i.e. it can contain a blank value). If $userid is passed with a blank $borrowernumber variable, the database will be checked for all instances of that userid (i.e. userid=? AND borrowernumber != '').
+
+ If $borrowernumber is provided, the database will be checked for every instance of that userid coupled with a different borrower(number) than the one provided.
+
+ return :
+ 0 for not unique (i.e. this $userid already exists)
+ 1 for unique (i.e. this $userid does not exist, or this $userid/$borrowernumber combination already exists)
+
+=cut
sub Check_Userid {
my ($uid,$member) = @_;
my $dbh = C4::Context->dbh;
- # Make sure the userid chosen is unique and not theirs if non-empty. If it is not,
- # Then we need to tell the user and have them create a new one.
my $sth =
$dbh->prepare(
"SELECT * FROM borrowers WHERE userid=? AND borrowernumber != ?");
}
}
+=head2 Generate_Userid
+
+ my $newuid = Generate_Userid($borrowernumber, $firstname, $surname);
+
+ Generate a userid using the $surname and the $firstname (if there is a value in $firstname).
+
+ $borrowernumber is optional (i.e. it can contain a blank value). A value is passed when generating a new userid for an existing borrower. When a new userid is created for a new borrower, a blank value is passed to this sub.
+
+ return :
+ new userid ($firstname.$surname if there is a $firstname, or $surname if there is no value in $firstname) plus offset (0 if the $newuid is unique, or a higher numeric value if Check_Userid finds an existing match for the $newuid in the database).
+
+=cut
+
sub Generate_Userid {
my ($borrowernumber, $firstname, $surname) = @_;
my $newuid;
my $offset = 0;
+ #The script will "do" the following code and increment the $offset until Check_Userid = 1 (i.e. until $newuid comes back as unique)
do {
$firstname =~ s/[[:digit:][:space:][:blank:][:punct:][:cntrl:]]//g;
$surname =~ s/[[:digit:][:space:][:blank:][:punct:][:cntrl:]]//g;
=cut
-my $ACCOUNT_TYPE_LENGTH = 5; # this is plain ridiculous...
-
-my @not_fines = ('Res');
-push @not_fines, 'Rent' unless C4::Context->preference('RentalsInNoissuesCharge');
-unless ( C4::Context->preference('ManInvInNoissuesCharge') ) {
- my $dbh = C4::Context->dbh;
- my $man_inv_types = $dbh->selectcol_arrayref(qq{SELECT authorised_value FROM authorised_values WHERE category = 'MANUAL_INV'});
- push @not_fines, map substr($_, 0, $ACCOUNT_TYPE_LENGTH), @$man_inv_types;
-}
-my %not_fine = map {$_ => 1} @not_fines;
-
sub GetMemberAccountBalance {
my ($borrowernumber) = @_;
+ my $ACCOUNT_TYPE_LENGTH = 5; # this is plain ridiculous...
+
+ my @not_fines = ('Res');
+ push @not_fines, 'Rent' unless C4::Context->preference('RentalsInNoissuesCharge');
+ unless ( C4::Context->preference('ManInvInNoissuesCharge') ) {
+ my $dbh = C4::Context->dbh;
+ my $man_inv_types = $dbh->selectcol_arrayref(qq{SELECT authorised_value FROM authorised_values WHERE category = 'MANUAL_INV'});
+ push @not_fines, map substr($_, 0, $ACCOUNT_TYPE_LENGTH), @$man_inv_types;
+ }
+ my %not_fine = map {$_ => 1} @not_fines;
+
my ($total, $acctlines) = GetMemberAccountRecords($borrowernumber);
my $other_charges = 0;
foreach (@$acctlines) {
}
}
+=head2 GetNoticeEmailAddress
+
+ $email = GetNoticeEmailAddress($borrowernumber);
+
+Return the email address of borrower used for notices, given the borrowernumber.
+Returns the empty string if no email address.
+
+=cut
+
+sub GetNoticeEmailAddress {
+ my $borrowernumber = shift;
+
+ my $which_address = C4::Context->preference("AutoEmailPrimaryAddress");
+ # if syspref is set to 'first valid' (value == OFF), look up email address
+ if ( $which_address eq 'OFF' ) {
+ return GetFirstValidEmailAddress($borrowernumber);
+ }
+ # specified email address field
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare( qq{
+ SELECT $which_address AS primaryemail
+ FROM borrowers
+ WHERE borrowernumber=?
+ } );
+ $sth->execute($borrowernumber);
+ my $data = $sth->fetchrow_hashref;
+ return $data->{'primaryemail'} || '';
+}
+
=head2 GetExpiryDate
$expirydate = GetExpiryDate($categorycode, $dateenrolled);
}
}
-=head2 checkuserpassword (OUEST-PROVENCE)
-
-check for the password and login are not used
-return the number of record
-0=> NOT USED 1=> USED
-
-=cut
-
-sub checkuserpassword {
- my ( $borrowernumber, $userid, $password ) = @_;
- $password = md5_base64($password);
- my $dbh = C4::Context->dbh;
- my $sth =
- $dbh->prepare(
-"Select count(*) from borrowers where borrowernumber !=? and userid =? and password=? "
- );
- $sth->execute( $borrowernumber, $userid, $password );
- my $number_rows = $sth->fetchrow;
- return $number_rows;
-
-}
-
=head2 GetborCatFromCatType
($codes_arrayref, $labels_hashref) = &GetborCatFromCatType();
SET dateexpiry='$date'
WHERE borrowernumber='$borrowerid'
EOF
- # add enrolmentfee if needed
- $sth = $dbh->prepare("SELECT enrolmentfee FROM categories WHERE categorycode=?");
- $sth->execute($borrower->{'categorycode'});
- my ($enrolmentfee) = $sth->fetchrow;
- if ($enrolmentfee && $enrolmentfee > 0) {
- # insert fee in patron debts
- manualinvoice($borrower->{'borrowernumber'}, '', '', 'A', $enrolmentfee);
- }
- logaction("MEMBERS", "RENEW", $borrower->{'borrowernumber'}, "Membership renewed")if C4::Context->preference("BorrowersLog");
+
+ AddEnrolmentFeeIfNeeded( $borrower->{categorycode}, $borrower->{borrowernumber} );
+
+ logaction("MEMBERS", "RENEW", $borrower->{'borrowernumber'}, "Membership renewed")if C4::Context->preference("BorrowersLog");
return $date if ($sth);
return 0;
}
=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;
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;
=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;
my $filterexpiry = $params->{'expired_before'};
my $filtercategory = $params->{'category_code'};
my $filterbranch = $params->{'branchcode'} ||
- ((C4::Context->preference('IndependantBranches')
+ ((C4::Context->preference('IndependentBranches')
&& C4::Context->userenv
- && C4::Context->userenv->{flags} % 2 !=1
+ && !C4::Context->IsSuperLibrarian()
&& C4::Context->userenv->{branch})
? C4::Context->userenv->{branch}
: "");
sub GetBorrowersWhoHaveNeverBorrowed {
my $filterbranch = shift ||
- ((C4::Context->preference('IndependantBranches')
+ ((C4::Context->preference('IndependentBranches')
&& C4::Context->userenv
- && C4::Context->userenv->{flags} % 2 !=1
+ && !C4::Context->IsSuperLibrarian()
&& C4::Context->userenv->{branch})
? C4::Context->userenv->{branch}
: "");
my $dbh = C4::Context->dbh;
my $date = shift ||POSIX::strftime("%Y-%m-%d",localtime());
my $filterbranch = shift ||
- ((C4::Context->preference('IndependantBranches')
+ ((C4::Context->preference('IndependentBranches')
&& C4::Context->userenv
- && C4::Context->userenv->{flags} % 2 !=1
+ && !C4::Context->IsSuperLibrarian()
&& C4::Context->userenv->{branch})
? C4::Context->userenv->{branch}
: "");
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 $issueslist = GetPendingIssues($borrowernumber);
foreach my $it (@$issueslist){
- if ((substr $it->{'issuedate'}, 0, 10) eq $now) {
+ if ((substr $it->{'issuedate'}, 0, 10) eq $now || (substr $it->{'lastreneweddate'}, 0, 10) eq $now) {
$it->{'now'} = 1;
}
elsif ((substr $it->{'date_due'}, 0, 10) le $now) {
$it->{'overdue'} = 1;
}
-
- $it->{'date_due'}=format_date($it->{'date_due'});
+ my $dt = dt_from_string( $it->{'date_due'} );
+ $it->{'date_due'} = output_pref( $dt );;
}
my @issues = sort { $b->{'timestamp'} <=> $a->{'timestamp'} } @$issueslist;
return ( $borrowernumber, $password );
}
+=head2 AddEnrolmentFeeIfNeeded
+
+ AddEnrolmentFeeIfNeeded( $borrower->{categorycode}, $borrower->{borrowernumber} );
+
+Add enrolment fee for a patron if needed.
+
+=cut
+
+sub AddEnrolmentFeeIfNeeded {
+ my ( $categorycode, $borrowernumber ) = @_;
+ # check for enrollment fee & add it if needed
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare(q{
+ SELECT enrolmentfee
+ FROM categories
+ WHERE categorycode=?
+ });
+ $sth->execute( $categorycode );
+ if ( $sth->err ) {
+ warn sprintf('Database returned the following error: %s', $sth->errstr);
+ return;
+ }
+ my ($enrolmentfee) = $sth->fetchrow;
+ if ($enrolmentfee && $enrolmentfee > 0) {
+ # insert fee in patron debts
+ C4::Accounts::manualinvoice( $borrowernumber, '', '', 'A', $enrolmentfee );
+ }
+}
+
+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;