Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 665741c4

Von Jan Büren vor fast 6 Jahren hinzugefügt

  • ID 665741c4bc6d05d6092a76383c1a444a3507921e
  • Vorgänger 07181f33
  • Nachfolger c301c2b3

BankTransaction: save_single_bank_transaction API-Änderung

S.a. POD und devel-Liste
Testfälle angepasst

Unterschiede anzeigen:

SL/Controller/BankTransaction.pm
558 558

  
559 559
  my $bank_transaction = $data{bank_transaction};
560 560

  
561
  # see pod
562
  if (@{ $bank_transaction->linked_invoices } || $bank_transaction->invoice_amount != 0) {
563
        return {
564
          %data,
565
          result  => 'error',
566
          message => $::locale->text("Bank transaction with id #1 has already been linked to one or more record and/or some amount is already assigned.", $bank_transaction->id),
567
        };
568
      }
569 561
  my (@warnings);
570 562

  
571 563
  my $worker = sub {
572 564
    my $bt_id                 = $data{bank_transaction_id};
573 565
    my $sign                  = $bank_transaction->amount < 0 ? -1 : 1;
574 566
    my $amount_of_transaction = $sign * $bank_transaction->amount;
567
    my $assigned_amount       = $sign * $bank_transaction->invoice_amount;
568
    my $not_assigned_amount   = $amount_of_transaction - $assigned_amount;
575 569
    my $payment_received      = $bank_transaction->amount > 0;
576 570
    my $payment_sent          = $bank_transaction->amount < 0;
577 571

  
572
    croak("No amount left to assign") if ($not_assigned_amount <= 0);
578 573

  
579 574
    foreach my $invoice_id (@{ $params{invoice_ids} }) {
580 575
      my $invoice = SL::DB::Manager::Invoice->find_by(id => $invoice_id) || SL::DB::Manager::PurchaseInvoice->find_by(id => $invoice_id);
......
634 629
      } else {
635 630
        $payment_type = 'without_skonto';
636 631
      };
637

  
638

  
639
      # pay invoice or go to the next bank transaction if the amount is not sufficiently high
640
      if ($invoice->open_amount <= $amount_of_transaction && $n_invoices < $max_invoices) {
641
        my $open_amount = ($payment_type eq 'with_skonto_pt'?$invoice->amount_less_skonto:$invoice->open_amount);
642
        # first calculate new bank transaction amount ...
643
        if ($invoice->is_sales) {
644
          $amount_of_transaction -= $sign * $open_amount;
645
          $bank_transaction->invoice_amount($bank_transaction->invoice_amount + $open_amount);
646
        } else {
647
          $amount_of_transaction += $sign * $open_amount;
648
          $bank_transaction->invoice_amount($bank_transaction->invoice_amount - $open_amount);
649
        }
650
        # ... and then pay the invoice
651
        my @acc_ids = $invoice->pay_invoice(chart_id => $bank_transaction->local_bank_account->chart_id,
652
                              trans_id     => $invoice->id,
653
                              amount       => $open_amount,
654
                              payment_type => $payment_type,
655
                              source       => $source,
656
                              memo         => $memo,
657
                              transdate    => $bank_transaction->transdate->to_kivitendo);
658
        # ... and record the origin via BankTransactionAccTrans
659
        if (scalar(@acc_ids) != 2) {
660
          return {
661
            %data,
662
            result  => 'error',
663
            message => $::locale->text("Unable to book transactions for bank purpose #1", $bank_transaction->purpose),
664
          };
665
        }
666
        foreach my $acc_trans_id (@acc_ids) {
667
            my $id_type = $invoice->is_sales ? 'ar' : 'ap';
668
            my  %props_acc = (
669
              acc_trans_id        => $acc_trans_id,
670
              bank_transaction_id => $bank_transaction->id,
671
              $id_type            => $invoice->id,
672
            );
673
            SL::DB::BankTransactionAccTrans->new(%props_acc)->save;
674
        }
675

  
676

  
677
      } else {
678
      # use the whole amount of the bank transaction for the invoice, overpay the invoice if necessary
679

  
680
        # $invoice->open_amount     is negative for credit_notes
681
        # $bank_transaction->amount is negative for outgoing transactions
682
        # so $amount_of_transaction is negative but needs positive
683
        # $invoice->open_amount may be negative for ap_transaction but may be positiv for negative ap_transaction
684
        # if $invoice->open_amount is negative $bank_transaction->amount is positve
685
        # if $invoice->open_amount is positive $bank_transaction->amount is negative
686
        # but amount of transaction is for both positive
687

  
688
        $amount_of_transaction *= -1 if ($invoice->amount < 0);
689

  
690
        # if we have a skonto case - the last invoice needs skonto
691
        $amount_of_transaction = $invoice->amount_less_skonto if ($payment_type eq 'with_skonto_pt');
692

  
693

  
694
        my $overpaid_amount = $amount_of_transaction - $invoice->open_amount;
695
        $invoice->pay_invoice(chart_id     => $bank_transaction->local_bank_account->chart_id,
696
                              trans_id     => $invoice->id,
697
                              amount       => $amount_of_transaction,
698
                              payment_type => $payment_type,
699
                              source       => $source,
700
                              memo         => $memo,
701
                              transdate    => $bank_transaction->transdate->to_kivitendo);
702
        $bank_transaction->invoice_amount($bank_transaction->amount);
703
        $amount_of_transaction = 0;
704

  
705
        if ($overpaid_amount >= 0.01) {
706
          push @warnings, {
707
            %data,
708
            result  => 'warning',
709
            message => $::locale->text('Invoice #1 was overpaid by #2.', $invoice->invnumber, $::form->format_amount(\%::myconfig, $overpaid_amount, 2)),
710
          };
711
        }
712
      }
632
    # pay invoice
633
    # TODO rewrite this: really booked amount should be a return value of Payment.pm
634
    # also this controller shouldnt care about how to calc skonto. we simply delegate the
635
    # payment_type to the helper and get the corresponding bank_transaction values back
636

  
637
    my $open_amount = ($payment_type eq 'with_skonto_pt' ? $invoice->amount_less_skonto : $invoice->open_amount);
638
    my $amount_for_booking = abs(($open_amount < $not_assigned_amount) ? $open_amount : $not_assigned_amount);
639
    $amount_for_booking *= $sign;
640
    $bank_transaction->invoice_amount($bank_transaction->invoice_amount + $amount_for_booking);
641

  
642
    # ... and then pay the invoice
643
    my @acc_ids = $invoice->pay_invoice(chart_id => $bank_transaction->local_bank_account->chart_id,
644
                          trans_id     => $invoice->id,
645
                          amount       => ($open_amount < $not_assigned_amount) ? $open_amount : $not_assigned_amount,
646
                          payment_type => $payment_type,
647
                          source       => $source,
648
                          memo         => $memo,
649
                          transdate    => $bank_transaction->transdate->to_kivitendo);
650
    # ... and record the origin via BankTransactionAccTrans
651
    if (scalar(@acc_ids) != 2) {
652
      return {
653
        %data,
654
        result  => 'error',
655
        message => $::locale->text("Unable to book transactions for bank purpose #1", $bank_transaction->purpose),
656
      };
657
    }
658
    foreach my $acc_trans_id (@acc_ids) {
659
        my $id_type = $invoice->is_sales ? 'ar' : 'ap';
660
        my  %props_acc = (
661
          acc_trans_id        => $acc_trans_id,
662
          bank_transaction_id => $bank_transaction->id,
663
          $id_type            => $invoice->id,
664
        );
665
        SL::DB::BankTransactionAccTrans->new(%props_acc)->save;
666
    }
713 667
      # Record a record link from the bank transaction to the invoice
714 668
      my %props = (
715 669
        from_table => 'bank_transactions',
......
965 919
C<invoice_ids>, an array ref of database IDs to purchase or sales
966 920
invoice objects).
967 921

  
922
This method handles already partly assigned bank transactions.
923

  
968 924
This method cannot handle already partly assigned bank transactions, i.e.
969 925
a bank transaction that has a invoice_amount <> 0 but not the fully
970 926
transaction amount (invoice_amount == amount).
971 927

  
972 928
If the amount of the bank transaction is higher than the sum of
973
the assigned invoices (1 .. n) the last invoice will be overpayed.
929
the assigned invoices (1 .. n) the bank transaction will only be
930
partly assigned.
974 931

  
975 932
The whole function is wrapped in a database transaction. If an
976 933
exception occurs the bank transaction is not posted at all. The same
t/bank/bank_transactions.t
1
use Test::More tests => 208;
1
use Test::More tests => 211;
2 2

  
3 3
use strict;
4 4

  
......
11 11
use List::Util qw(sum);
12 12

  
13 13
use SL::DB::AccTransaction;
14
use SL::DB::BankTransactionAccTrans;
14 15
use SL::DB::Buchungsgruppe;
15 16
use SL::DB::Currency;
16 17
use SL::DB::Customer;
......
34 35

  
35 36
sub clear_up {
36 37

  
38
  SL::DB::Manager::BankTransactionAccTrans->delete_all(all => 1);
37 39
  SL::DB::Manager::BankTransaction->delete_all(all => 1);
38 40
  SL::DB::Manager::InvoiceItem->delete_all(all => 1);
39 41
  SL::DB::Manager::InvoiceItem->delete_all(all => 1);
......
442 444
  $ar_transaction->load;
443 445
  $bt->load;
444 446

  
445
  is($ar_transaction->paid                     , '135.00000' , "$testname: 'salesinv overpaid' was overpaid");
446
  is($bt->invoice_amount                       , '135.00000' , "$testname: bt invoice amount was assigned overpaid amount");
447
  is($ar_transaction->paid                     , '119.00000' , "$testname: 'salesinv overpaid' was not overpaid");
448
  is($bt->invoice_amount                       , '119.00000' , "$testname: bt invoice amount was not fully assigned with the overpaid amount");
447 449
{ local $TODO = 'this currently fails because closed ignores over-payments, see commit d90966c7';
448 450
  is($ar_transaction->closed                   , 0           , "$testname: 'salesinv overpaid' is open (via 'closed' method')");
449 451
}
450
  is($ar_transaction->open_amount == 0 ? 1 : 0 , 0           , "$testname: 'salesinv overpaid is open (via amount-paid)");
452
  is($ar_transaction->open_amount == 0 ? 1 : 0 , 1           , "$testname: 'salesinv overpaid is closed (via amount-paid)");
451 453

  
452 454
};
453 455

  
454 456
sub test_overpayment_with_partialpayment {
455 457

  
456
  # two payments on different days, 10 and 119. If there is only one invoice we want it be overpaid.
458
  # two payments on different days, 10 and 119. If there is only one invoice we
459
  # don't want it to be overpaid.
457 460
  my $testname = 'test_overpayment_with_partialpayment';
458 461

  
459 462
  $ar_transaction = test_ar_transaction(invnumber => 'salesinv overpaid partial');
......
473 476
  };
474 477
  save_btcontroller_to_string();
475 478

  
479
  $bt_1->load;
480
  is($bt_1->invoice_amount ,  '10.00000' , "$testname: bt_1 invoice amount was fully assigned");
476 481
  $::form->{invoice_ids} = {
477 482
    $bt_2->id => [ $ar_transaction->id ]
478 483
  };
479 484
  save_btcontroller_to_string();
480 485

  
481 486
  $ar_transaction->load;
482
  $bt_1->load;
483 487
  $bt_2->load;
484 488

  
485
  is($ar_transaction->paid , '129.00000' , "$testname: 'salesinv overpaid partial' was overpaid");
486
  is($bt_1->invoice_amount ,  '10.00000' , "$testname: bt_1 invoice amount was assigned overpaid amount");
487
  is($bt_2->invoice_amount , '119.00000' , "$testname: bt_2 invoice amount was assigned overpaid amount");
489
  is($bt_1->invoice_amount ,  '10.00000' , "$testname: bt_1 invoice amount was fully assigned");
490
  is($ar_transaction->paid , '119.00000' , "$testname: 'salesinv overpaid partial' was not overpaid");
491
  is($bt_2->invoice_amount , '109.00000' , "$testname: bt_2 invoice amount was partly assigned");
488 492

  
489 493
};
490 494

  
......
585 589
                                              bank_chart_id => $bank->id,
586 590
                                              transdate     => DateTime->today->add(days => 10),
587 591
                                                               );
592

  
588 593
  my ($agreement, $rule_matches) = $bt->get_agreement_with_invoice($invoice);
589 594
  is($agreement, 15, "points for negative ap transaction ok");
590 595

  
......
601 606
  is($invoice->netamount, '-20.00000', "$testname: netamount ok");
602 607
  is($invoice->paid     , '-23.80000', "$testname: paid ok");
603 608
  is($bt->invoice_amount, '23.80000', "$testname: bt invoice amount for ap was assigned");
609
  is($bt->amount,         '23.80000', "$testname: bt  amount for ap was assigned");
604 610

  
605 611
  return $invoice;
606 612
};

Auch abrufbar als: Unified diff