Bug 22564: Remove references to 'Rep' accounttype
[koha-equinox.git] / Koha / Account.pm
index 036b34e..9d66e70 100644 (file)
@@ -23,12 +23,17 @@ use Carp;
 use Data::Dumper;
 use List::MoreUtils qw( uniq );
 
+use C4::Circulation qw( ReturnLostItem );
+use C4::Letters;
 use C4::Log qw( logaction );
 use C4::Stats qw( UpdateStats );
 
+use Koha::Patrons;
 use Koha::Account::Lines;
 use Koha::Account::Offsets;
 use Koha::DateUtils qw( dt_from_string );
+use Koha::Exceptions;
+use Koha::Exceptions::Account;
 
 =head1 NAME
 
@@ -79,18 +84,10 @@ sub pay {
 
     my $userenv = C4::Context->userenv;
 
-    # We should remove accountno, it is no longer needed
-    my $last = Koha::Account::Lines->search(
-        {
-            borrowernumber => $self->{patron_id}
-        },
-        {
-            order_by => 'accountno'
-        }
-    )->next();
-    my $accountno = $last ? $last->accountno + 1 : 1;
+    my $patron = Koha::Patrons->find( $self->{patron_id} );
 
     my $manager_id = $userenv ? $userenv->{number} : 0;
+    my $interface = $params ? ( $params->{interface} || C4::Context->interface ) : C4::Context->interface;
 
     my @fines_paid; # List of account lines paid on with this payment
 
@@ -111,7 +108,7 @@ sub pay {
         $fine->amountoutstanding($new_amountoutstanding)->store();
         $balance_remaining = $balance_remaining - $amount_to_pay;
 
-        if ( $fine->itemnumber && $fine->accounttype && ( $fine->accounttype eq 'Rep' || $fine->accounttype eq 'L' ) )
+        if ( $fine->itemnumber && $fine->accounttype && ( $fine->accounttype eq 'L' ) )
         {
             C4::Circulation::ReturnLostItem( $self->{patron_id}, $fine->itemnumber );
         }
@@ -137,11 +134,11 @@ sub pay {
                         new_amountoutstanding => 0,
                         amount_paid           => $old_amountoutstanding,
                         accountlines_id       => $fine->id,
-                        accountno             => $fine->accountno,
                         manager_id            => $manager_id,
                         note                  => $note,
                     }
-                )
+                ),
+                $interface
             );
             push( @fines_paid, $fine->id );
         }
@@ -151,9 +148,8 @@ sub pay {
     # than the what was owed on the given line. In that case pay down other
     # lines with remaining balance.
     my @outstanding_fines;
-    @outstanding_fines = Koha::Account::Lines->search(
+    @outstanding_fines = $self->lines->search(
         {
-            borrowernumber    => $self->{patron_id},
             amountoutstanding => { '>' => 0 },
         }
     ) if $balance_remaining > 0;
@@ -189,11 +185,11 @@ sub pay {
                         new_amountoutstanding => $fine->amountoutstanding,
                         amount_paid           => $amount_to_pay,
                         accountlines_id       => $fine->id,
-                        accountno             => $fine->accountno,
                         manager_id            => $manager_id,
                         note                  => $note,
                     }
-                )
+                ),
+                $interface
             );
             push( @fines_paid, $fine->id );
         }
@@ -212,7 +208,6 @@ sub pay {
     my $payment = Koha::Account::Line->new(
         {
             borrowernumber    => $self->{patron_id},
-            accountno         => $accountno,
             date              => dt_from_string(),
             amount            => 0 - $amount,
             description       => $description,
@@ -220,6 +215,8 @@ sub pay {
             payment_type      => $payment_type,
             amountoutstanding => 0 - $balance_remaining,
             manager_id        => $manager_id,
+            interface         => $interface,
+            branchcode        => $library_id,
             note              => $note,
         }
     )->store();
@@ -229,15 +226,12 @@ sub pay {
         $o->store();
     }
 
-    $library_id ||= $userenv ? $userenv->{'branch'} : undef;
-
     UpdateStats(
         {
             branch         => $library_id,
             type           => $type,
             amount         => $amount,
             borrowernumber => $self->{patron_id},
-            accountno      => $accountno,
         }
     );
 
@@ -249,17 +243,45 @@ sub pay {
                 {
                     action            => "create_$type",
                     borrowernumber    => $self->{patron_id},
-                    accountno         => $accountno,
                     amount            => 0 - $amount,
                     amountoutstanding => 0 - $balance_remaining,
                     accounttype       => $account_type,
                     accountlines_paid => \@fines_paid,
                     manager_id        => $manager_id,
                 }
-            )
+            ),
+            $interface
         );
     }
 
+    if ( C4::Context->preference('UseEmailReceipts') ) {
+        if (
+            my $letter = C4::Letters::GetPreparedLetter(
+                module                 => 'circulation',
+                letter_code            => uc("ACCOUNT_$type"),
+                message_transport_type => 'email',
+                lang    => $patron->lang,
+                tables => {
+                    borrowers       => $self->{patron_id},
+                    branches        => $self->{library_id},
+                },
+                substitute => {
+                    credit => $payment,
+                    offsets => \@account_offsets,
+                },
+              )
+          )
+        {
+            C4::Letters::EnqueueLetter(
+                {
+                    letter                 => $letter,
+                    borrowernumber         => $self->{patron_id},
+                    message_transport_type => 'email',
+                }
+            ) or warn "can't enqueue letter $letter";
+        }
+    }
+
     return $payment->id;
 }
 
@@ -273,6 +295,7 @@ my $credit_line = Koha::Account->new({ patron_id => $patron_id })->add_credit(
         description  => $description,
         note         => $note,
         user_id      => $user_id,
+        interface    => $interface,
         library_id   => $library_id,
         sip          => $sip,
         payment_type => $payment_type,
@@ -299,15 +322,22 @@ sub add_credit {
     my $description  = $params->{description} // q{};
     my $note         = $params->{note} // q{};
     my $user_id      = $params->{user_id};
+    my $interface    = $params->{interface};
     my $library_id   = $params->{library_id};
     my $sip          = $params->{sip};
     my $payment_type = $params->{payment_type};
     my $type         = $params->{type} || 'payment';
     my $item_id      = $params->{item_id};
 
+    unless ( $interface ) {
+        Koha::Exceptions::MissingParameter->throw(
+            error => 'The interface parameter is mandatory'
+        );
+    }
+
     my $schema = Koha::Database->new->schema;
 
-    my $account_type = $Koha::Account::account_type->{$type};
+    my $account_type = $Koha::Account::account_type_credit->{$type};
     $account_type .= $sip
         if defined $sip &&
            $type eq 'payment';
@@ -316,10 +346,6 @@ sub add_credit {
 
     $schema->txn_do(
         sub {
-            # We should remove accountno, it is no longer needed
-            my $last = Koha::Account::Lines->search( { borrowernumber => $self->{patron_id} },
-                { order_by => 'accountno' } )->next();
-            my $accountno = $last ? $last->accountno + 1 : 1;
 
             # Insert the account line
             $line = Koha::Account::Line->new(
@@ -332,7 +358,9 @@ sub add_credit {
                     payment_type      => $payment_type,
                     note              => $note,
                     manager_id        => $user_id,
-                    itemnumber        => $item_id
+                    interface         => $interface,
+                    branchcode        => $library_id,
+                    itemnumber        => $item_id,
                 }
             )->store();
 
@@ -349,7 +377,6 @@ sub add_credit {
                     type           => $type,
                     amount         => $amount,
                     borrowernumber => $self->{patron_id},
-                    accountno      => $accountno,
                 }
             ) if grep { $type eq $_ } ('payment', 'writeoff') ;
 
@@ -360,7 +387,6 @@ sub add_credit {
                     Dumper(
                         {   action            => "create_$type",
                             borrowernumber    => $self->{patron_id},
-                            accountno         => $accountno,
                             amount            => $amount,
                             description       => $description,
                             amountoutstanding => $amount,
@@ -368,8 +394,136 @@ sub add_credit {
                             note              => $note,
                             itemnumber        => $item_id,
                             manager_id        => $user_id,
+                            branchcode        => $library_id,
                         }
-                    )
+                    ),
+                    $interface
+                );
+            }
+        }
+    );
+
+    return $line;
+}
+
+=head3 add_debit
+
+This method allows adding debits to a patron's account
+
+my $debit_line = Koha::Account->new({ patron_id => $patron_id })->add_debit(
+    {
+        amount       => $amount,
+        description  => $description,
+        note         => $note,
+        user_id      => $user_id,
+        interface    => $interface,
+        library_id   => $library_id,
+        type         => $debit_type,
+        item_id      => $item_id,
+        issue_id     => $issue_id
+    }
+);
+
+$debit_type can be any of:
+  - overdue
+  - lost_item
+  - new_card
+  - account
+  - sundry
+  - processing
+  - rent
+  - reserve
+  - manual
+
+=cut
+
+sub add_debit {
+
+    my ( $self, $params ) = @_;
+
+    # amount should always be a positive value
+    my $amount       = $params->{amount};
+
+    unless ( $amount > 0 ) {
+        Koha::Exceptions::Account::AmountNotPositive->throw(
+            error => 'Debit amount passed is not positive'
+        );
+    }
+
+    my $description  = $params->{description} // q{};
+    my $note         = $params->{note} // q{};
+    my $user_id      = $params->{user_id};
+    my $interface    = $params->{interface};
+    my $library_id   = $params->{library_id};
+    my $type         = $params->{type};
+    my $item_id      = $params->{item_id};
+    my $issue_id     = $params->{issue_id};
+
+    unless ( $interface ) {
+        Koha::Exceptions::MissingParameter->throw(
+            error => 'The interface parameter is mandatory'
+        );
+    }
+
+    my $schema = Koha::Database->new->schema;
+
+    unless ( exists($Koha::Account::account_type_debit->{$type}) ) {
+        Koha::Exceptions::Account::UnrecognisedType->throw(
+            error => 'Type of debit not recognised'
+        );
+    }
+
+    my $account_type = $Koha::Account::account_type_debit->{$type};
+
+    my $line;
+
+    $schema->txn_do(
+        sub {
+
+            # Insert the account line
+            $line = Koha::Account::Line->new(
+                {   borrowernumber    => $self->{patron_id},
+                    date              => \'NOW()',
+                    amount            => $amount,
+                    description       => $description,
+                    accounttype       => $account_type,
+                    amountoutstanding => $amount,
+                    payment_type      => undef,
+                    note              => $note,
+                    manager_id        => $user_id,
+                    interface         => $interface,
+                    itemnumber        => $item_id,
+                    issue_id          => $issue_id,
+                    branchcode        => $library_id,
+                    ( $type eq 'overdue' ? ( status => 'UNRETURNED' ) : ()),
+                }
+            )->store();
+
+            # Record the account offset
+            my $account_offset = Koha::Account::Offset->new(
+                {   debit_id => $line->id,
+                    type      => $Koha::Account::offset_type->{$type},
+                    amount    => $amount
+                }
+            )->store();
+
+            if ( C4::Context->preference("FinesLog") ) {
+                logaction(
+                    "FINES", 'CREATE',
+                    $self->{patron_id},
+                    Dumper(
+                        {   action            => "create_$type",
+                            borrowernumber    => $self->{patron_id},
+                            amount            => $amount,
+                            description       => $description,
+                            amountoutstanding => $amount,
+                            accounttype       => $account_type,
+                            note              => $note,
+                            itemnumber        => $item_id,
+                            manager_id        => $user_id,
+                        }
+                    ),
+                    $interface
                 );
             }
         }
@@ -388,57 +542,51 @@ Return the balance (sum of amountoutstanding columns)
 
 sub balance {
     my ($self) = @_;
-    my $fines = Koha::Account::Lines->search(
-        {
-            borrowernumber => $self->{patron_id},
-        },
-        {
-            select => [ { sum => 'amountoutstanding' } ],
-            as => ['total_amountoutstanding'],
-        }
-    );
-
-    return ( $fines->count )
-      ? $fines->next->get_column('total_amountoutstanding') + 0
-      : 0;
+    return $self->lines->total_outstanding;
 }
 
 =head3 outstanding_debits
 
 my $lines = Koha::Account->new({ patron_id => $patron_id })->outstanding_debits;
 
+It returns the debit lines with outstanding amounts for the patron.
+
+In scalar context, it returns a Koha::Account::Lines iterator. In list context, it will
+return a list of Koha::Account::Line objects.
+
 =cut
 
 sub outstanding_debits {
     my ($self) = @_;
 
-    my $lines = Koha::Account::Lines->search(
+    return $self->lines->search(
         {
-            borrowernumber    => $self->{patron_id},
+            amount            => { '>' => 0 },
             amountoutstanding => { '>' => 0 }
         }
     );
-
-    return $lines;
 }
 
 =head3 outstanding_credits
 
 my $lines = Koha::Account->new({ patron_id => $patron_id })->outstanding_credits;
 
+It returns the credit lines with outstanding amounts for the patron.
+
+In scalar context, it returns a Koha::Account::Lines iterator. In list context, it will
+return a list of Koha::Account::Line objects.
+
 =cut
 
 sub outstanding_credits {
     my ($self) = @_;
 
-    my $lines = Koha::Account::Lines->search(
+    return $self->lines->search(
         {
-            borrowernumber    => $self->{patron_id},
+            amount            => { '<' => 0 },
             amountoutstanding => { '<' => 0 }
         }
     );
-
-    return $lines;
 }
 
 =head3 non_issues_charges
@@ -476,19 +624,58 @@ sub non_issues_charges {
     }
     @not_fines = map { substr( $_, 0, $ACCOUNT_TYPE_LENGTH ) } uniq(@not_fines);
 
-    my $non_issues_charges = Koha::Account::Lines->search(
+    return $self->lines->search(
         {
-            borrowernumber => $self->{patron_id},
             accounttype    => { -not_in => \@not_fines }
         },
+    )->total_outstanding;
+}
+
+=head3 lines
+
+my $lines = $self->lines;
+
+Return all credits and debits for the user, outstanding or otherwise
+
+=cut
+
+sub lines {
+    my ($self) = @_;
+
+    return Koha::Account::Lines->search(
         {
-            select => [ { sum => 'amountoutstanding' } ],
-            as     => ['non_issues_charges'],
+            borrowernumber => $self->{patron_id},
         }
     );
-    return $non_issues_charges->count
-      ? $non_issues_charges->next->get_column('non_issues_charges') + 0
-      : 0;
+}
+
+=head3 reconcile_balance
+
+$account->reconcile_balance();
+
+Find outstanding credits and use them to pay outstanding debits.
+Currently, this implicitly uses the 'First In First Out' rule for
+applying credits against debits.
+
+=cut
+
+sub reconcile_balance {
+    my ($self) = @_;
+
+    my $outstanding_debits  = $self->outstanding_debits;
+    my $outstanding_credits = $self->outstanding_credits;
+
+    while (     $outstanding_debits->total_outstanding > 0
+            and my $credit = $outstanding_credits->next )
+    {
+        # there's both outstanding debits and credits
+        $credit->apply( { debits => $outstanding_debits } );    # applying credit, no special offset
+
+        $outstanding_debits = $self->outstanding_debits;
+
+    }
+
+    return $self;
 }
 
 1;
@@ -502,16 +689,24 @@ sub non_issues_charges {
 our $offset_type = {
     'credit'           => 'Manual Credit',
     'forgiven'         => 'Writeoff',
-    'lost_item_return' => 'Lost Item Return',
+    'lost_item_return' => 'Lost Item',
     'payment'          => 'Payment',
-    'writeoff'         => 'Writeoff'
+    'writeoff'         => 'Writeoff',
+    'account'          => 'Account Fee',
+    'reserve'          => 'Reserve Fee',
+    'processing'       => 'Processing Fee',
+    'lost_item'        => 'Lost Item',
+    'rent'             => 'Rental Fee',
+    'overdue'          => 'OVERDUE',
+    'manual_debit'     => 'Manual Debit',
+    'hold_expired'     => 'Hold Expired'
 };
 
-=head3 $account_type
+=head3 $account_type_credit
 
 =cut
 
-our $account_type = {
+our $account_type_credit = {
     'credit'           => 'C',
     'forgiven'         => 'FOR',
     'lost_item_return' => 'CR',
@@ -519,8 +714,29 @@ our $account_type = {
     'writeoff'         => 'W'
 };
 
-=head1 AUTHOR
+=head3 $account_type_debit
+
+=cut
+
+our $account_type_debit = {
+    'account'       => 'A',
+    'overdue'       => 'OVERDUE',
+    'lost_item'     => 'L',
+    'new_card'      => 'N',
+    'sundry'        => 'M',
+    'processing'    => 'PF',
+    'rent'          => 'Rent',
+    'reserve'       => 'Res',
+    'manual_debit'  => 'M',
+    'hold_expired'  => 'HE'
+};
+
+=head1 AUTHORS
+
+=encoding utf8
 
 Kyle M Hall <kyle.m.hall@gmail.com>
+Tomás Cohen Arazi <tomascohen@gmail.com>
+Martin Renvoize <martin.renvoize@ptfs-europe.com>
 
 =cut