Bug 23843: Add mapping to Koha::Account::Line
[koha-ffzg.git] / Koha / Account / Line.pm
index caba957..ae7bbc1 100644 (file)
@@ -22,22 +22,38 @@ use Data::Dumper;
 
 use C4::Log qw(logaction);
 
+use Koha::Account::Offsets;
 use Koha::Database;
+use Koha::Exceptions::Account;
 use Koha::Items;
-use Koha::Account::Offsets;
 
 use base qw(Koha::Object);
 
+=encoding utf8
+
 =head1 NAME
 
-Koha::Account::Lines - Koha accountline Object class
+Koha::Account::Line - Koha accountline Object class
 
 =head1 API
 
-=head2 Class Methods
+=head2 Class methods
+
+=cut
+
+=head3 patron
+
+Return the patron linked to this account line
 
 =cut
 
+sub patron {
+    my ( $self ) = @_;
+    my $rs = $self->_result->borrowernumber;
+    return unless $rs;
+    return Koha::Patron->_new_from_dbic( $rs );
+}
+
 =head3 item
 
 Return the item linked to this account line if exists
@@ -47,12 +63,32 @@ Return the item linked to this account line if exists
 sub item {
     my ( $self ) = @_;
     my $rs = $self->_result->itemnumber;
+    return unless $rs;
     return Koha::Item->_new_from_dbic( $rs );
 }
 
+=head3 checkout
+
+Return the checkout linked to this account line if exists
+
+=cut
+
+sub checkout {
+    my ( $self ) = @_;
+    return unless $self->issue_id ;
+
+    $self->{_checkout} ||= Koha::Checkouts->find( $self->issue_id );
+    $self->{_checkout} ||= Koha::Old::Checkouts->find( $self->issue_id );
+    return $self->{_checkout};
+}
+
 =head3 void
 
-$payment_accountline->void();
+  $payment_accountline->void();
+
+Used to 'void' (or reverse) a payment/credit. It will roll back any offsets
+created by the application of this credit upon any debits and mark the credit
+as 'void' by updating it's status to "VOID".
 
 =cut
 
@@ -114,7 +150,7 @@ sub void {
 
             $self->set(
                 {
-                    accounttype       => 'VOID',
+                    status            => 'VOID',
                     amountoutstanding => 0,
                     amount            => 0,
                 }
@@ -125,6 +161,268 @@ sub void {
 
 }
 
+=head3 apply
+
+    my $debits = $account->outstanding_debits;
+    my $outstanding_amount = $credit->apply( { debits => $debits, [ offset_type => $offset_type ] } );
+
+Applies the credit to a given debits array reference.
+
+=head4 arguments hashref
+
+=over 4
+
+=item debits - Koha::Account::Lines object set of debits
+
+=item offset_type (optional) - a string indicating the offset type (valid values are those from
+the 'account_offset_types' table)
+
+=back
+
+=cut
+
+sub apply {
+    my ( $self, $params ) = @_;
+
+    my $debits      = $params->{debits};
+    my $offset_type = $params->{offset_type} // 'Credit Applied';
+
+    unless ( $self->is_credit ) {
+        Koha::Exceptions::Account::IsNotCredit->throw(
+            error => 'Account line ' . $self->id . ' is not a credit'
+        );
+    }
+
+    my $available_credit = $self->amountoutstanding * -1;
+
+    unless ( $available_credit > 0 ) {
+        Koha::Exceptions::Account::NoAvailableCredit->throw(
+            error => 'Outstanding credit is ' . $available_credit . ' and cannot be applied'
+        );
+    }
+
+    my $schema = Koha::Database->new->schema;
+
+    $schema->txn_do( sub {
+        for my $debit ( @{$debits} ) {
+
+            unless ( $debit->is_debit ) {
+                Koha::Exceptions::Account::IsNotDebit->throw(
+                    error => 'Account line ' . $debit->id . 'is not a debit'
+                );
+            }
+            my $amount_to_cancel;
+            my $owed = $debit->amountoutstanding;
+
+            if ( $available_credit >= $owed ) {
+                $amount_to_cancel = $owed;
+            }
+            else {    # $available_credit < $debit->amountoutstanding
+                $amount_to_cancel = $available_credit;
+            }
+
+            # record the account offset
+            Koha::Account::Offset->new(
+                {   credit_id => $self->id,
+                    debit_id  => $debit->id,
+                    amount    => $amount_to_cancel * -1,
+                    type      => $offset_type,
+                }
+            )->store();
+
+            $available_credit -= $amount_to_cancel;
+
+            $self->amountoutstanding( $available_credit * -1 )->store;
+            $debit->amountoutstanding( $owed - $amount_to_cancel )->store;
+
+            # Same logic exists in Koha::Account::pay
+            if (   $debit->amountoutstanding == 0
+                && $debit->itemnumber
+                && $debit->accounttype
+                && $debit->accounttype eq 'LOST' )
+            {
+                C4::Circulation::ReturnLostItem( $self->borrowernumber, $debit->itemnumber );
+            }
+
+        }
+    });
+
+    return $available_credit;
+}
+
+=head3 adjust
+
+This method allows updating a debit or credit on a patron's account
+
+    $account_line->adjust(
+        {
+            amount    => $amount,
+            type      => $update_type,
+            interface => $interface
+        }
+    );
+
+$update_type can be any of:
+  - overdue_update
+
+Authors Note: The intention here is that this method is only used
+to adjust accountlines where the final amount is not yet known/fixed.
+Incrementing fines are the only existing case at the time of writing,
+all other forms of 'adjustment' should be recorded as distinct credits
+or debits and applied, via an offset, to the corresponding debit or credit.
+
+=cut
+
+sub adjust {
+    my ( $self, $params ) = @_;
+
+    my $amount       = $params->{amount};
+    my $update_type  = $params->{type};
+    my $interface    = $params->{interface};
+
+    unless ( exists($Koha::Account::Line::allowed_update->{$update_type}) ) {
+        Koha::Exceptions::Account::UnrecognisedType->throw(
+            error => 'Update type not recognised'
+        );
+    }
+
+    my $account_type   = $self->accounttype;
+    my $account_status = $self->status;
+    unless (
+        (
+            exists(
+                $Koha::Account::Line::allowed_update->{$update_type}
+                  ->{$account_type}
+            )
+            && ( $Koha::Account::Line::allowed_update->{$update_type}
+                ->{$account_type} eq $account_status )
+        )
+      )
+    {
+        Koha::Exceptions::Account::UnrecognisedType->throw(
+            error => 'Update type not allowed on this accounttype' );
+    }
+
+    my $schema = Koha::Database->new->schema;
+
+    $schema->txn_do(
+        sub {
+
+            my $amount_before             = $self->amount;
+            my $amount_outstanding_before = $self->amountoutstanding;
+            my $difference                = $amount - $amount_before;
+            my $new_outstanding           = $amount_outstanding_before + $difference;
+
+            my $offset_type = $account_type;
+            $offset_type .= ( $difference > 0 ) ? "_INCREASE" : "_DECREASE";
+
+            # Catch cases that require patron refunds
+            if ( $new_outstanding < 0 ) {
+                my $account =
+                  Koha::Patrons->find( $self->borrowernumber )->account;
+                my $credit = $account->add_credit(
+                    {
+                        amount      => $new_outstanding * -1,
+                        description => 'Overpayment refund',
+                        type        => 'credit',
+                        interface   => $interface,
+                        ( $update_type eq 'overdue_update' ? ( item_id => $self->itemnumber ) : ()),
+                    }
+                );
+                $new_outstanding = 0;
+            }
+
+            # Update the account line
+            $self->set(
+                {
+                    date              => \'NOW()',
+                    amount            => $amount,
+                    amountoutstanding => $new_outstanding,
+                }
+            )->store();
+
+            # Record the account offset
+            my $account_offset = Koha::Account::Offset->new(
+                {
+                    debit_id => $self->id,
+                    type     => $offset_type,
+                    amount   => $difference
+                }
+            )->store();
+
+            if ( C4::Context->preference("FinesLog") ) {
+                logaction(
+                    "FINES", 'UPDATE', #undef becomes UPDATE in UpdateFine
+                    $self->borrowernumber,
+                    Dumper(
+                        {   action            => $update_type,
+                            borrowernumber    => $self->borrowernumber,
+                            amount            => $amount,
+                            description       => undef,
+                            amountoutstanding => $new_outstanding,
+                            accounttype       => $self->accounttype,
+                            note              => undef,
+                            itemnumber        => $self->itemnumber,
+                            manager_id        => undef,
+                        }
+                    )
+                ) if ( $update_type eq 'overdue_update' );
+            }
+        }
+    );
+
+    return $self;
+}
+
+=head3 is_credit
+
+    my $bool = $line->is_credit;
+
+=cut
+
+sub is_credit {
+    my ($self) = @_;
+
+    return ( $self->amount < 0 );
+}
+
+=head3 is_debit
+
+    my $bool = $line->is_debit;
+
+=cut
+
+sub is_debit {
+    my ($self) = @_;
+
+    return !$self->is_credit;
+}
+
+=head3 to_api_mapping
+
+This method returns the mapping for representing a Koha::Account::Line object
+on the API.
+
+=cut
+
+sub to_api_mapping {
+    return {
+        accountlines_id   => 'account_line_id',
+        accounttype       => 'account_type',
+        amountoutstanding => 'amount_outstanding',
+        borrowernumber    => 'patron_id',
+        branchcode        => 'library_id',
+        issue_id          => 'checkout_id',
+        itemnumber        => 'item_id',
+        manager_id        => 'user_id',
+        note              => 'internal_note',
+    };
+}
+
+=head2 Internal methods
+
+=cut
+
 =head3 _type
 
 =cut
@@ -134,3 +432,19 @@ sub _type {
 }
 
 1;
+
+=head2 Name mappings
+
+=head3 $allowed_update
+
+=cut
+
+our $allowed_update = { 'overdue_update' => { 'OVERDUE' => 'UNRETURNED' } };
+
+=head1 AUTHORS
+
+Kyle M Hall <kyle@bywatersolutions.com >
+Tomás Cohen Arazi <tomascohen@theke.io>
+Martin Renvoize <martin.renvoize@ptfs-europe.com>
+
+=cut