View | Details | Raw Unified | Return to bug 27636
Collapse All | Expand All

(-)a/Koha/Account.pm (+88 lines)
Lines 485-490 sub add_credit { Link Here
485
    return $line;
485
    return $line;
486
}
486
}
487
487
488
=head3 payin_amount
489
490
    my $credit = $account->payin_amount(
491
        {
492
            amount          => $amount,
493
            credit_type     => $credit_type,
494
            payment_type    => $payment_type,
495
            cash_register   => $register_id,
496
            interface       => $interface,
497
            library_id      => $branchcode,
498
            user_id         => $staff_id,
499
            debits          => $debit_lines,
500
            description     => $description,
501
            note            => $note
502
        }
503
    );
504
505
This method allows an amount to be paid into a patrons account and immediately applied against debts.
506
507
You can optionally pass a debts parameter which consists of an arrayref of Koha::Account::Line debit lines.
508
509
$credit_type can be any of:
510
  - 'PAYMENT'
511
  - 'WRITEOFF'
512
  - 'FORGIVEN'
513
514
=cut
515
516
sub payin_amount {
517
    my ( $self, $params ) = @_;
518
519
    # check for mandatory params
520
    my @mandatory = ( 'interface', 'amount', 'type' );
521
    for my $param (@mandatory) {
522
        unless ( defined( $params->{$param} ) ) {
523
            Koha::Exceptions::MissingParameter->throw(
524
                error => "The $param parameter is mandatory" );
525
        }
526
    }
527
528
    # Check for mandatory register
529
    Koha::Exceptions::Account::RegisterRequired->throw()
530
      if ( C4::Context->preference("UseCashRegisters")
531
        && ( $params->{payment_type} eq 'CASH' )
532
        && !defined($params->{cash_register}) );
533
534
    # amount should always be passed as a positive value
535
    my $amount = $params->{amount};
536
    unless ( $amount > 0 ) {
537
        Koha::Exceptions::Account::AmountNotPositive->throw(
538
            error => 'Payin amount passed is not positive' );
539
    }
540
541
    my $credit;
542
    my $schema = Koha::Database->new->schema;
543
    $schema->txn_do(
544
        sub {
545
546
            # Add payin credit
547
            $credit = $self->add_credit($params);
548
549
            # Offset debts passed first
550
            if ( exists( $params->{debits} ) ) {
551
                $credit = $credit->apply(
552
                    {
553
                        debits      => $params->{debits},
554
                        offset_type => $params->{type}
555
                    }
556
                );
557
            }
558
559
            # Offset against remaining balance if AutoReconcile
560
            if ( C4::Context->preference("AccountAutoReconcile")
561
                && $credit->amountoutstanding != 0 )
562
            {
563
                $credit = $credit->apply(
564
                    {
565
                        debits      => [ $self->outstanding_debits->as_list ],
566
                        offset_type => $params->{type}
567
                    }
568
                );
569
            }
570
        }
571
    );
572
573
    return $credit;
574
}
575
488
=head3 add_debit
576
=head3 add_debit
489
577
490
This method allows adding debits to a patron's account
578
This method allows adding debits to a patron's account
(-)a/Koha/Account/Line.pm (-3 / +10 lines)
Lines 602-608 the 'account_offset_types' table) Link Here
602
602
603
sub apply {
603
sub apply {
604
    my ( $self, $params ) = @_;
604
    my ( $self, $params ) = @_;
605
606
    my $debits      = $params->{debits};
605
    my $debits      = $params->{debits};
607
    my $offset_type = $params->{offset_type} // 'Credit Applied';
606
    my $offset_type = $params->{offset_type} // 'Credit Applied';
608
607
Lines 657-663 sub apply { Link Here
657
            # Attempt to renew the item associated with this debit if
656
            # Attempt to renew the item associated with this debit if
658
            # appropriate
657
            # appropriate
659
            if ($debit->renewable) {
658
            if ($debit->renewable) {
660
                $debit->renew_item($params->{interface});
659
                my $outcome = $debit->renew_item($params->{interface});
660
                $self->add_message(
661
                    {
662
                        message => 'renew_trigger',
663
                        payload => $outcome
664
                    }
665
                );
661
            }
666
            }
662
667
663
            # Same logic exists in Koha::Account::pay
668
            # Same logic exists in Koha::Account::pay
Lines 677-686 sub apply { Link Here
677
                C4::Circulation::ReturnLostItem( $self->borrowernumber,
682
                C4::Circulation::ReturnLostItem( $self->borrowernumber,
678
                    $debit->itemnumber );
683
                    $debit->itemnumber );
679
            }
684
            }
685
686
            last if $available_credit == 0;
680
        }
687
        }
681
    });
688
    });
682
689
683
    return $available_credit;
690
    return $self;
684
}
691
}
685
692
686
=head3 payout
693
=head3 payout
(-)a/t/db_dependent/Koha/Account.t (-2 / +128 lines)
Lines 19-25 Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Test::More tests => 14;
22
use Test::More tests => 15;
23
use Test::MockModule;
23
use Test::MockModule;
24
use Test::Exception;
24
use Test::Exception;
25
25
Lines 1272-1274 subtest 'Koha::Account::payout_amount() tests' => sub { Link Here
1272
1272
1273
    $schema->storage->txn_rollback;
1273
    $schema->storage->txn_rollback;
1274
};
1274
};
1275
- 
1275
1276
subtest 'Koha::Account::payin_amount() tests' => sub {
1277
    plan tests => 36;
1278
1279
    $schema->storage->txn_begin;
1280
1281
    # delete logs and statistics
1282
    my $action_logs = $schema->resultset('ActionLog')->search()->count;
1283
    my $statistics  = $schema->resultset('Statistic')->search()->count;
1284
1285
    my $staff = $builder->build_object( { class => 'Koha::Patrons' } );
1286
    my $library = $builder->build_object( { class => 'Koha::Libraries' } );
1287
    my $register =
1288
      $builder->build_object( { class => 'Koha::Cash::Registers' } );
1289
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
1290
    my $account =
1291
      Koha::Account->new( { patron_id => $patron->borrowernumber } );
1292
1293
    is( $account->balance, 0, 'Test patron has no balance' );
1294
1295
    my $payin_params = {
1296
        type  => 'PAYMENT',
1297
        payment_type => 'CASH',
1298
        branch       => $library->id,
1299
        register_id  => $register->id,
1300
        staff_id     => $staff->id,
1301
        interface    => 'intranet',
1302
        amount       => -10,
1303
    };
1304
1305
    my @required_fields =
1306
      ( 'interface', 'amount', 'type' );
1307
    for my $required_field (@required_fields) {
1308
        my $this_payin = { %{$payin_params} };
1309
        delete $this_payin->{$required_field};
1310
1311
        throws_ok {
1312
            $account->payin_amount($this_payin);
1313
        }
1314
        'Koha::Exceptions::MissingParameter',
1315
          "Exception thrown if $required_field parameter missing";
1316
    }
1317
1318
    throws_ok {
1319
        $account->payin_amount($payin_params);
1320
    }
1321
    'Koha::Exceptions::Account::AmountNotPositive',
1322
      'Expected validation exception thrown (amount not positive)';
1323
1324
    $payin_params->{amount} = 10;
1325
1326
    # Enable cash registers
1327
    t::lib::Mocks::mock_preference( 'UseCashRegisters', 1 );
1328
    throws_ok {
1329
        $account->payin_amount($payin_params);
1330
    }
1331
    'Koha::Exceptions::Account::RegisterRequired',
1332
'Exception thrown for UseCashRegisters:1 + payment_type:CASH + cash_register:undef';
1333
1334
    # Disable cash registers
1335
    t::lib::Mocks::mock_preference( 'UseCashRegisters', 0 );
1336
1337
    # Enable AccountAutoReconcile
1338
    t::lib::Mocks::mock_preference( 'AccountAutoReconcile', 1 );
1339
1340
    # Add some outstanding debits
1341
    my $debit_1 = $account->add_debit( { amount => 2,  interface => 'commandline', type => 'OVERDUE' } );
1342
    my $debit_2 = $account->add_debit( { amount => 3,  interface => 'commandline', type => 'OVERDUE' } );
1343
    my $debit_3 = $account->add_debit( { amount => 5,  interface => 'commandline', type => 'OVERDUE' } );
1344
    my $debit_4 = $account->add_debit( { amount => 10, interface => 'commandline', type => 'OVERDUE' } );
1345
    my $debits = $account->outstanding_debits();
1346
    is( $debits->count, 4, "Found 4 debits with outstanding amounts" );
1347
    is( $debits->total_outstanding + 0, 20, "Total 20 outstanding debit" );
1348
1349
    my $payin = $account->payin_amount($payin_params);
1350
    is(ref($payin), 'Koha::Account::Line', 'Return the Koha::Account::Line object for the payin');
1351
    is($payin->amount + 0, -10, "Payin amount recorded correctly");
1352
    is($payin->amountoutstanding + 0, 0, "Full amount was used to pay debts");
1353
    $debits = $account->outstanding_debits();
1354
    is($debits->count, 1, "Payin was applied against oldest outstanding debits first");
1355
    is($debits->total_outstanding + 0, 10, "Total of 10 outstanding debit remaining");
1356
1357
    my $offsets = Koha::Account::Offsets->search( { credit_id => $payin->id } );
1358
    is( $offsets->count, 4, 'Four offsets generated' );
1359
    my $offset = $offsets->next;
1360
    is( $offset->type, 'Payment', 'Payment offset added for payin line' );
1361
    is( $offset->amount * 1, -10, 'Correct offset amount recorded' );
1362
    $offset = $offsets->next;
1363
    is( $offset->debit_id, $debit_1->id, "Offset added against debit_1");
1364
    is( $offset->type,       'PAYMENT', "Payment used for offset_type" );
1365
    is( $offset->amount * 1, -2,      'Correct amount offset against debit_1' );
1366
    $offset = $offsets->next;
1367
    is( $offset->debit_id, $debit_2->id, "Offset added against debit_2");
1368
    is( $offset->type,       'PAYMENT', "Payment used for offset_type" );
1369
    is( $offset->amount * 1, -3,      'Correct amount offset against debit_2' );
1370
    $offset = $offsets->next;
1371
    is( $offset->debit_id, $debit_3->id, "Offset added against debit_3");
1372
    is( $offset->type,       'PAYMENT', "Payment used for offset_type" );
1373
    is( $offset->amount * 1, -5,      'Correct amount offset against debit_3' );
1374
1375
    my $debit_5 = $account->add_debit( { amount => 5, interface => 'commandline', type => 'OVERDUE' } );
1376
    $debits = $account->outstanding_debits();
1377
    is($debits->count, 2, "New debit added");
1378
    $payin_params->{amount} = 2.50;
1379
    $payin_params->{debits} = [$debit_5];
1380
    $payin = $account->payin_amount($payin_params);
1381
1382
    $debits = $account->outstanding_debits();
1383
    is($debits->count, 2, "Second debit not fully paid off");
1384
    is($debits->total_outstanding + 0, 12.50, "12.50 outstanding debit remaining");
1385
    $debit_4->discard_changes;
1386
    $debit_5->discard_changes;
1387
    is($debit_4->amountoutstanding + 0, 10, "Debit 4 unaffected when debit_5 was passed to payin_amount");
1388
    is($debit_5->amountoutstanding + 0, 2.50, "Debit 5 correctly reduced when payin_amount called with debit_5 passed");
1389
1390
    $offsets = Koha::Account::Offsets->search( { credit_id => $payin->id } );
1391
    is( $offsets->count, 2, 'Two offsets generated' );
1392
    $offset = $offsets->next;
1393
    is( $offset->type, 'Payment', 'Payment offset added for payin line' );
1394
    is( $offset->amount * 1, -2.50, 'Correct offset amount recorded' );
1395
    $offset = $offsets->next;
1396
    is( $offset->debit_id, $debit_5->id, "Offset added against debit_5");
1397
    is( $offset->type,       'PAYMENT', "PAYMENT used for offset_type" );
1398
    is( $offset->amount * 1, -2.50,      'Correct amount offset against debit_5' );
1399
1400
    $schema->storage->txn_rollback;
1401
};

Return to bug 27636