Bug 8129 - Quickslips not printing
[koha.git] / C4 / Members.pm
index 56718f0..ce54952 100644 (file)
@@ -23,97 +23,106 @@ package C4::Members;
 use strict;
 #use warnings; FIXME - Bug 2505
 use C4::Context;
-use C4::Dates qw(format_date_in_iso);
+use C4::Dates qw(format_date_in_iso format_date);
 use Digest::MD5 qw(md5_base64);
-use Date::Calc qw/Today Add_Delta_YM/;
+use Date::Calc qw/Today Add_Delta_YM check_date Date_to_Days/;
 use C4::Log; # logaction
 use C4::Overdues;
 use C4::Reserves;
 use C4::Accounts;
 use C4::Biblio;
+use C4::Letters;
 use C4::SQLHelper qw(InsertInTable UpdateInTable SearchInTable);
 use C4::Members::Attributes qw(SearchIdMatchingAttribute);
+use C4::NewsChannels; #get slip news
+use DateTime;
+use DateTime::Format::DateParse;
+use Koha::DateUtils;
 
 our ($VERSION,@ISA,@EXPORT,@EXPORT_OK,$debug);
 
 BEGIN {
-       $VERSION = 3.02;
-       $debug = $ENV{DEBUG} || 0;
-       require Exporter;
-       @ISA = qw(Exporter);
-       #Get data
-       push @EXPORT, qw(
-               &Search
-               &GetMemberDetails
+    $VERSION = 3.02;
+    $debug = $ENV{DEBUG} || 0;
+    require Exporter;
+    @ISA = qw(Exporter);
+    #Get data
+    push @EXPORT, qw(
+        &Search
+        &GetMemberDetails
         &GetMemberRelatives
-               &GetMember
+        &GetMember
+
+        &GetGuarantees
 
-               &GetGuarantees 
+        &GetMemberIssuesAndFines
+        &GetPendingIssues
+        &GetAllIssues
 
-               &GetMemberIssuesAndFines
-               &GetPendingIssues
-               &GetAllIssues
+        &get_institutions
+        &getzipnamecity
+        &getidcity
 
-               &get_institutions 
-               &getzipnamecity 
-               &getidcity
+        &GetFirstValidEmailAddress
 
-                &GetFirstValidEmailAddress
+        &GetAge
+        &GetCities
+        &GetRoadTypes
+        &GetRoadTypeDetails
+        &GetSortDetails
+        &GetTitles
 
-               &GetAge 
-               &GetCities 
-               &GetRoadTypes 
-               &GetRoadTypeDetails 
-               &GetSortDetails
-               &GetTitles
+        &GetPatronImage
+        &PutPatronImage
+        &RmPatronImage
 
-    &GetPatronImage
-    &PutPatronImage
-    &RmPatronImage
+        &GetHideLostItemsPreference
 
-                &GetHideLostItemsPreference
+        &IsMemberBlocked
+        &GetMemberAccountRecords
+        &GetBorNotifyAcctRecord
 
-               &IsMemberBlocked
-               &GetMemberAccountRecords
-               &GetBorNotifyAcctRecord
+        &GetborCatFromCatType
+        &GetBorrowercategory
+        GetBorrowerCategorycode
+        &GetBorrowercategoryList
 
-               &GetborCatFromCatType 
-               &GetBorrowercategory
-    &GetBorrowercategoryList
+        &GetBorrowersWhoHaveNotBorrowedSince
+        &GetBorrowersWhoHaveNeverBorrowed
+        &GetBorrowersWithIssuesHistoryOlderThan
 
-               &GetBorrowersWhoHaveNotBorrowedSince
-               &GetBorrowersWhoHaveNeverBorrowed
-               &GetBorrowersWithIssuesHistoryOlderThan
+        &GetExpiryDate
 
-               &GetExpiryDate
+        &AddMessage
+        &DeleteMessage
+        &GetMessages
+        &GetMessagesCount
 
-               &AddMessage
-               &DeleteMessage
-               &GetMessages
-               &GetMessagesCount
-       );
+        &IssueSlip
+        GetBorrowersWithEmail
+    );
 
-       #Modify data
-       push @EXPORT, qw(
-               &ModMember
-               &changepassword
+    #Modify data
+    push @EXPORT, qw(
+        &ModMember
+        &changepassword
          &ModPrivacy
-       );
-
-       #Delete data
-       push @EXPORT, qw(
-               &DelMember
-       );
-
-       #Insert data
-       push @EXPORT, qw(
-               &AddMember
-               &add_member_orgs
-               &MoveMemberToDeleted
-               &ExtendMemberSubscriptionTo
-       );
-
-       #Check data
+    );
+
+    #Delete data
+    push @EXPORT, qw(
+        &DelMember
+    );
+
+    #Insert data
+    push @EXPORT, qw(
+        &AddMember
+        &add_member_orgs
+        &MoveMemberToDeleted
+        &ExtendMemberSubscriptionTo
+    );
+
+    #Check data
     push @EXPORT, qw(
         &checkuniquemember
         &checkuserpassword
@@ -313,15 +322,15 @@ sub GetMemberDetails {
     my $query;
     my $sth;
     if ($borrowernumber) {
-        $sth = $dbh->prepare("SELECT borrowers.*,category_type,categories.description,reservefee FROM borrowers LEFT JOIN categories ON borrowers.categorycode=categories.categorycode WHERE  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->execute($borrowernumber);
     }
     elsif ($cardnumber) {
-        $sth = $dbh->prepare("SELECT borrowers.*,category_type,categories.description,reservefee FROM borrowers LEFT JOIN categories ON borrowers.categorycode=categories.categorycode WHERE 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->execute($cardnumber);
     }
     else {
-        return undef;
+        return;
     }
     my $borrower = $sth->fetchrow_hashref;
     my ($amount) = GetMemberAccountRecords( $borrowernumber);
@@ -340,14 +349,16 @@ sub GetMemberDetails {
     $borrower->{'flags'}     = $flags;
     $borrower->{'authflags'} = $accessflagshash;
 
-    # find out how long the membership lasts
-    $sth =
-      $dbh->prepare(
-        "select enrolmentperiod from categories where categorycode = ?");
-    $sth->execute( $borrower->{'categorycode'} );
-    my $enrolment = $sth->fetchrow;
-    $borrower->{'enrolmentperiod'} = $enrolment;
-    
+    # For the purposes of making templates easier, we'll define a
+    # 'showname' which is the alternate form the user's first name if 
+    # 'other name' is defined.
+    if ($borrower->{category_type} eq 'I') {
+        $borrower->{'showname'} = $borrower->{'othernames'};
+        $borrower->{'showname'} .= " $borrower->{'firstname'}" if $borrower->{'firstname'};
+    } else {
+        $borrower->{'showname'} = $borrower->{'firstname'};
+    }
+
     return ($borrower);    #, $flags, $accessflagshash);
 }
 
@@ -449,13 +460,15 @@ sub patronflags {
         $flaginfo{'noissues'} = 1;
         $flags{'LOST'}        = \%flaginfo;
     }
-    if (   $patroninformation->{'debarred'}
-        && $patroninformation->{'debarred'} == 1 )
-    {
-        my %flaginfo;
-        $flaginfo{'message'}  = 'Borrower is Debarred.';
-        $flaginfo{'noissues'} = 1;
-        $flags{'DBARRED'}     = \%flaginfo;
+    if ( $patroninformation->{'debarred'} && check_date( split( /-/, $patroninformation->{'debarred'} ) ) ) {
+        if ( Date_to_Days(Date::Calc::Today) < Date_to_Days( split( /-/, $patroninformation->{'debarred'} ) ) ) {
+            my %flaginfo;
+            $flaginfo{'debarredcomment'} = $patroninformation->{'debarredcomment'};
+            $flaginfo{'message'}         = $patroninformation->{'debarredcomment'};
+            $flaginfo{'noissues'}        = 1;
+            $flaginfo{'dateend'}         = $patroninformation->{'debarred'};
+            $flags{'DBARRED'}           = \%flaginfo;
+        }
     }
     if (   $patroninformation->{'borrowernotes'}
         && $patroninformation->{'borrowernotes'} )
@@ -464,7 +477,7 @@ sub patronflags {
         $flaginfo{'message'} = $patroninformation->{'borrowernotes'};
         $flags{'NOTES'}      = \%flaginfo;
     }
-    my ( $odues, $itemsoverdue ) = checkoverdues($patroninformation->{'borrowernumber'});
+    my ( $odues, $itemsoverdue ) = C4::Overdues::checkoverdues($patroninformation->{'borrowernumber'});
     if ( $odues && $odues > 0 ) {
         my %flaginfo;
         $flaginfo{'message'}  = "Yes";
@@ -569,7 +582,7 @@ sub GetMemberRelatives {
     $sth->execute($borrowernumber);
     my $data = $sth->fetchrow_arrayref();
     push @glist, $data->[0] if $data->[0];
-    my $guarantor = $data->[0] if $data->[0];
+    my $guarantor = $data->[0] ? $data->[0] : undef;
 
     # Getting guarantees
     $query = "SELECT borrowernumber FROM borrowers WHERE guarantorid=?";
@@ -621,50 +634,23 @@ sub IsMemberBlocked {
     my $borrowernumber = shift;
     my $dbh            = C4::Context->dbh;
 
-    # does patron have current fine days?
-       my $strsth=qq{
-            SELECT
-            ADDDATE(returndate, finedays * DATEDIFF(returndate,date_due) ) AS blockingdate,
-            DATEDIFF(ADDDATE(returndate, finedays * DATEDIFF(returndate,date_due)),NOW()) AS blockedcount
-            FROM old_issues
-       };
-    if(C4::Context->preference("item-level_itypes")){
-        $strsth.=
-               qq{ LEFT JOIN items ON (items.itemnumber=old_issues.itemnumber)
-            LEFT JOIN issuingrules ON (issuingrules.itemtype=items.itype)}
-    }else{
-        $strsth .= 
-               qq{ LEFT JOIN items ON (items.itemnumber=old_issues.itemnumber)
-            LEFT JOIN biblioitems ON (biblioitems.biblioitemnumber=items.biblioitemnumber)
-            LEFT JOIN issuingrules ON (issuingrules.itemtype=biblioitems.itemtype) };
-    }
-       $strsth.=
-        qq{ WHERE finedays IS NOT NULL
-            AND  date_due < returndate
-            AND borrowernumber = ?
-            ORDER BY blockingdate DESC, blockedcount DESC
-            LIMIT 1};
-       my $sth=$dbh->prepare($strsth);
-    $sth->execute($borrowernumber);
-    my $row = $sth->fetchrow_hashref;
-    my $blockeddate  = $row->{'blockeddate'};
-    my $blockedcount = $row->{'blockedcount'};
+    my $blockeddate = CheckBorrowerDebarred($borrowernumber);
 
-    return (1, $blockedcount) if $blockedcount > 0;
+    return ( 1, $blockeddate ) if $blockeddate;
 
     # if he have late issues
-    $sth = $dbh->prepare(
+    my $sth = $dbh->prepare(
         "SELECT COUNT(*) as latedocs
          FROM issues
          WHERE borrowernumber = ?
-         AND date_due < curdate()"
+         AND date_due < now()"
     );
     $sth->execute($borrowernumber);
     my $latedocs = $sth->fetchrow_hashref->{'latedocs'};
 
-    return (-1, $latedocs) if $latedocs > 0;
+    return ( -1, $latedocs ) if $latedocs > 0;
 
-    return (0, 0);
+    return ( 0, 0 );
 }
 
 =head2 GetMemberIssuesAndFines
@@ -695,7 +681,7 @@ sub GetMemberIssuesAndFines {
     $sth = $dbh->prepare(
         "SELECT COUNT(*) FROM issues 
          WHERE borrowernumber = ? 
-         AND date_due < curdate()"
+         AND date_due < now()"
     );
     $sth->execute($borrowernumber);
     my $overdue_count = $sth->fetchrow_arrayref->[0];
@@ -861,7 +847,7 @@ mode, to avoid database corruption.
 use vars qw( @weightings );
 my @weightings = ( 8, 4, 6, 3, 5, 2, 1 );
 
-sub fixup_cardnumber ($) {
+sub fixup_cardnumber {
     my ($cardnumber) = @_;
     my $autonumber_members = C4::Context->boolean_preference('autoMemberNum') || 0;
 
@@ -1012,7 +998,6 @@ sub GetPendingIssues {
     # must avoid biblioitems.* to prevent large marc and marcxml fields from killing performance
     # FIXME: namespace collision: each table has "timestamp" fields.  Which one is "timestamp" ?
     # FIXME: circ/ciculation.pl tries to sort by timestamp!
-    # FIXME: C4::Print::printslip tries to sort by timestamp!
     # FIXME: namespace collision: other collisions possible.
     # FIXME: most of this data isn't really being used by callers.
     my $query =
@@ -1050,9 +1035,15 @@ sub GetPendingIssues {
     my $sth = C4::Context->dbh->prepare($query);
     $sth->execute(@borrowernumbers);
     my $data = $sth->fetchall_arrayref({});
-    my $today = C4::Dates->new->output('iso');
+    my $tz = C4::Context->tz();
+    my $today = DateTime->now( time_zone => $tz);
     foreach (@{$data}) {
-        if ($_->{date_due}  and $_->{date_due} lt $today) {
+        if ($_->{issuedate}) {
+            $_->{issuedate} = dt_from_string($_->{issuedate}, 'sql');
+        }
+        $_->{date_due} or next;
+        $_->{date_due} = DateTime::Format::DateParse->parse_datetime($_->{date_due}, $tz->name());
+        if ( DateTime->compare($_->{date_due}, $today) == -1 ) {
             $_->{overdue} = 1;
         }
     }
@@ -1193,6 +1184,11 @@ sub GetBorNotifyAcctRecord {
     $sth->execute( $borrowernumber, $notifyid );
     my $total = 0;
     while ( my $data = $sth->fetchrow_hashref ) {
+        if ( $data->{itemnumber} ) {
+            my $biblio = GetBiblioFromItemNumber( $data->{itemnumber} );
+            $data->{biblionumber} = $biblio->{biblionumber};
+            $data->{title}        = $biblio->{title};
+        }
         $acctlines[$numlines] = $data;
         $numlines++;
         $total += int(100 * $data->{'amountoutstanding'});
@@ -1418,10 +1414,6 @@ sub GetborCatFromCatType {
 Given the borrower's category code, the function returns the corresponding
 data hashref for a comprehensive information display.
 
-  $arrayref_hashref = &GetBorrowercategory;
-
-If no category code provided, the function returns all the categories.
-
 =cut
 
 sub GetBorrowercategory {
@@ -1442,6 +1434,26 @@ sub GetBorrowercategory {
     return;  
 }    # sub getborrowercategory
 
+
+=head2 GetBorrowerCategorycode
+
+    $categorycode = &GetBorrowerCategoryCode( $borrowernumber );
+
+Given the borrowernumber, the function returns the corresponding categorycode
+=cut
+
+sub GetBorrowerCategorycode {
+    my ( $borrowernumber ) = @_;
+    my $dbh = C4::Context->dbh;
+    my $sth = $dbh->prepare( qq{
+        SELECT categorycode
+        FROM borrowers
+        WHERE borrowernumber = ?
+    } );
+    $sth->execute( $borrowernumber );
+    return $sth->fetchrow;
+}
+
 =head2 GetBorrowercategoryList
 
   $arrayref_hashref = &GetBorrowercategoryList;
@@ -1710,7 +1722,9 @@ sub ExtendMemberSubscriptionTo {
     my $dbh = C4::Context->dbh;
     my $borrower = GetMember('borrowernumber'=>$borrowerid);
     unless ($date){
-      $date=POSIX::strftime("%Y-%m-%d",localtime());
+      $date = (C4::Context->preference('BorrowerRenewalPeriodBase') eq 'dateexpiry') ?
+                                        C4::Dates->new($borrower->{'dateexpiry'}, 'iso')->output("iso") :
+                                        C4::Dates->new()->output("iso");
       $date = GetExpiryDate( $borrower->{'categorycode'}, $date );
     }
     my $sth = $dbh->do(<<EOF);
@@ -2076,7 +2090,7 @@ sub GetBorrowersNamesAndLatestIssue {
 
 =head2 DebarMember
 
-  my $success = DebarMember( $borrowernumber );
+my $success = DebarMember( $borrowernumber, $todate );
 
 marks a Member as debarred, and therefore unable to checkout any more
 items.
@@ -2088,13 +2102,16 @@ true on success, false on failure
 
 sub DebarMember {
     my $borrowernumber = shift;
+    my $todate         = shift;
 
     return unless defined $borrowernumber;
     return unless $borrowernumber =~ /^\d+$/;
 
-    return ModMember( borrowernumber => $borrowernumber,
-                      debarred       => 1 );
-    
+    return ModMember(
+        borrowernumber => $borrowernumber,
+        debarred       => $todate
+    );
+
 }
 
 =head2 ModPrivacy
@@ -2146,7 +2163,7 @@ sub AddMessage {
     my $query = "INSERT INTO messages ( borrowernumber, branchcode, message_type, message ) VALUES ( ?, ?, ?, ? )";
     my $sth = $dbh->prepare($query);
     $sth->execute( $borrowernumber, $branchcode, $message_type, $message );
-
+    logaction("MEMBERS", "ADDCIRCMESSAGE", $borrowernumber, $message) if C4::Context->preference("BorrowersLog");
     return 1;
 }
 
@@ -2236,13 +2253,120 @@ sub DeleteMessage {
     my ( $message_id ) = @_;
 
     my $dbh = C4::Context->dbh;
-
-    my $query = "DELETE FROM messages WHERE message_id = ?";
+    my $query = "SELECT * FROM messages WHERE message_id = ?";
     my $sth = $dbh->prepare($query);
     $sth->execute( $message_id );
+    my $message = $sth->fetchrow_hashref();
+
+    $query = "DELETE FROM messages WHERE message_id = ?";
+    $sth = $dbh->prepare($query);
+    $sth->execute( $message_id );
+    logaction("MEMBERS", "DELCIRCMESSAGE", $message->{'borrowernumber'}, $message->{'message'}) if C4::Context->preference("BorrowersLog");
+}
+
+=head2 IssueSlip
+
+  IssueSlip($branchcode, $borrowernumber, $quickslip)
+
+  Returns letter hash ( see C4::Letters::GetPreparedLetter )
+
+  $quickslip is boolean, to indicate whether we want a quick slip
+
+=cut
+
+sub IssueSlip {
+    my ($branch, $borrowernumber, $quickslip) = @_;
+
+#   return unless ( C4::Context->boolean_preference('printcirculationslips') );
+
+    my $now       = POSIX::strftime("%Y-%m-%d", localtime);
+
+    my $issueslist = GetPendingIssues($borrowernumber);
+    foreach my $it (@$issueslist){
+        if ((substr $it->{'issuedate'}, 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 @issues = sort { $b->{'timestamp'} <=> $a->{'timestamp'} } @$issueslist;
+
+    my ($letter_code, %repeat);
+    if ( $quickslip ) {
+        $letter_code = 'ISSUEQSLIP';
+        %repeat =  (
+            'checkedout' => [ map {
+                'biblio' => $_,
+                'items'  => $_,
+                'issues' => $_,
+            }, grep { $_->{'now'} } @issues ],
+        );
+    }
+    else {
+        $letter_code = 'ISSUESLIP';
+        %repeat =  (
+            'checkedout' => [ map {
+                'biblio' => $_,
+                'items'  => $_,
+                'issues' => $_,
+            }, grep { !$_->{'overdue'} } @issues ],
+
+            'overdue' => [ map {
+                'biblio' => $_,
+                'items'  => $_,
+                'issues' => $_,
+            }, grep { $_->{'overdue'} } @issues ],
+
+            'news' => [ map {
+                $_->{'timestamp'} = $_->{'newdate'};
+                { opac_news => $_ }
+            } @{ GetNewsToDisplay("slip") } ],
+        );
+    }
+
+    return  C4::Letters::GetPreparedLetter (
+        module => 'circulation',
+        letter_code => $letter_code,
+        branchcode => $branch,
+        tables => {
+            'branches'    => $branch,
+            'borrowers'   => $borrowernumber,
+        },
+        repeat => \%repeat,
+    );
+}
+
+=head2 GetBorrowersWithEmail
+
+    ([$borrnum,$userid], ...) = GetBorrowersWithEmail('me@example.com');
+
+This gets a list of users and their basic details from their email address.
+As it's possible for multiple user to have the same email address, it provides
+you with all of them. If there is no userid for the user, there will be an
+C<undef> there. An empty list will be returned if there are no matches.
+
+=cut
 
+sub GetBorrowersWithEmail {
+    my $email = shift;
+
+    my $dbh = C4::Context->dbh;
+
+    my $query = "SELECT borrowernumber, userid FROM borrowers WHERE email=?";
+    my $sth=$dbh->prepare($query);
+    $sth->execute($email);
+    my @result = ();
+    while (my $ref = $sth->fetch) {
+        push @result, $ref;
+    }
+    die "Failure searching for borrowers by email address: $sth->errstr" if $sth->err;
+    return @result;
 }
 
+
 END { }    # module clean-up code here (global destructor)
 
 1;