BankTransaction: save_single_bank_transaction API-Änderung
[kivitendo-erp.git] / t / bank / bank_transactions.t
1 use Test::More tests => 211;
2
3 use strict;
4
5 use lib 't';
6 use utf8;
7
8 use Carp;
9 use Support::TestSetup;
10 use Test::Exception;
11 use List::Util qw(sum);
12
13 use SL::DB::AccTransaction;
14 use SL::DB::BankTransactionAccTrans;
15 use SL::DB::Buchungsgruppe;
16 use SL::DB::Currency;
17 use SL::DB::Customer;
18 use SL::DB::Vendor;
19 use SL::DB::Invoice;
20 use SL::DB::Unit;
21 use SL::DB::Part;
22 use SL::DB::TaxZone;
23 use SL::DB::BankAccount;
24 use SL::DB::PaymentTerm;
25 use SL::DB::PurchaseInvoice;
26 use SL::DB::BankTransaction;
27 use SL::Controller::BankTransaction;
28 use SL::Dev::ALL qw(:ALL);
29 use Data::Dumper;
30
31 my ($customer, $vendor, $currency_id, $unit, $tax, $tax0, $tax7, $tax_9, $payment_terms, $bank_account);
32 my ($transdate1, $transdate2, $currency);
33 my ($ar_chart,$bank,$ar_amount_chart, $ap_chart, $ap_amount_chart);
34 my ($ar_transaction, $ap_transaction);
35
36 sub clear_up {
37
38   SL::DB::Manager::BankTransactionAccTrans->delete_all(all => 1);
39   SL::DB::Manager::BankTransaction->delete_all(all => 1);
40   SL::DB::Manager::InvoiceItem->delete_all(all => 1);
41   SL::DB::Manager::InvoiceItem->delete_all(all => 1);
42   SL::DB::Manager::Invoice->delete_all(all => 1);
43   SL::DB::Manager::PurchaseInvoice->delete_all(all => 1);
44   SL::DB::Manager::Part->delete_all(all => 1);
45   SL::DB::Manager::Customer->delete_all(all => 1);
46   SL::DB::Manager::Vendor->delete_all(all => 1);
47   SL::DB::Manager::SepaExportItem->delete_all(all => 1);
48   SL::DB::Manager::SepaExport->delete_all(all => 1);
49   SL::DB::Manager::BankAccount->delete_all(all => 1);
50   SL::DB::Manager::PaymentTerm->delete_all(all => 1);
51   SL::DB::Manager::Currency->delete_all(where => [ name => 'CUR' ]);
52 };
53
54 my $bt_controller;
55
56 sub save_btcontroller_to_string {
57   my $output;
58   open(my $outputFH, '>', \$output) or die;
59   my $oldFH = select $outputFH;
60
61   $bt_controller = SL::Controller::BankTransaction->new;
62   $bt_controller->action_save_invoices;
63
64   select $oldFH;
65   close $outputFH;
66   return $output;
67 }
68
69 # starting test:
70 Support::TestSetup::login();
71
72 clear_up();
73 reset_state(); # initialise customers/vendors/bank/currency/...
74
75 test1();
76
77 test_overpayment_with_partialpayment();
78 test_overpayment();
79 reset_state();
80 test_skonto_exact();
81 test_two_invoices();
82 test_partial_payment();
83 test_credit_note();
84 test_ap_transaction();
85 test_neg_ap_transaction(invoice => 0);
86 test_neg_ap_transaction(invoice => 1);
87 test_ap_payment_transaction();
88 test_ap_payment_part_transaction();
89 test_neg_sales_invoice();
90 test_two_neg_ap_transaction();
91 test_one_inv_and_two_invoices_with_skonto_exact();
92 test_bt_error();
93
94 reset_state();
95 test_sepa_export();
96
97 reset_state();
98 test_bt_rule1();
99 reset_state();
100 test_two_banktransactions();
101 # remove all created data at end of test
102 clear_up();
103
104 done_testing();
105
106 ###### functions for setting up data
107
108 sub reset_state {
109   my %params = @_;
110
111   $params{$_} ||= {} for qw(unit customer tax vendor);
112
113   clear_up();
114
115   $transdate1 = DateTime->today;
116   $transdate2 = DateTime->today->add(days => 5);
117
118   $tax             = SL::DB::Manager::Tax->find_by(taxkey => 3, rate => 0.19, %{ $params{tax} }) || croak "No tax";
119   $tax7            = SL::DB::Manager::Tax->find_by(taxkey => 2, rate => 0.07)                    || croak "No tax for 7\%";
120   $tax_9           = SL::DB::Manager::Tax->find_by(taxkey => 9, rate => 0.19, %{ $params{tax} }) || croak "No tax for 19\%";
121   $tax0            = SL::DB::Manager::Tax->find_by(taxkey => 0, rate => 0.0)                     || croak "No tax for 0\%";
122
123   $currency_id     = $::instance_conf->get_currency_id;
124
125   $bank_account     =  SL::DB::BankAccount->new(
126     account_number  => '123',
127     bank_code       => '123',
128     iban            => '123',
129     bic             => '123',
130     bank            => '123',
131     chart_id        => SL::DB::Manager::Chart->find_by(description => 'Bank')->id,
132     name            => SL::DB::Manager::Chart->find_by(description => 'Bank')->description,
133   )->save;
134
135   $customer = new_customer(
136     name                      => 'Test Customer OLÉ S.L. Årdbärg AB',
137     iban                      => 'DE12500105170648489890',
138     bic                       => 'TESTBIC',
139     account_number            => '648489890',
140     mandate_date_of_signature => $transdate1,
141     mandator_id               => 'foobar',
142     bank                      => 'Geizkasse',
143     bank_code                 => 'G1235',
144     depositor                 => 'Test Customer',
145     customernumber            => 'CUST1704',
146   )->save;
147
148   $payment_terms = create_payment_terms();
149
150   $vendor = new_vendor(
151     name           => 'Test Vendor',
152     payment_id     => $payment_terms->id,
153     iban           => 'DE12500105170648489890',
154     bic            => 'TESTBIC',
155     account_number => '648489890',
156     bank           => 'Geizkasse',
157     bank_code      => 'G1235',
158     depositor      => 'Test Vendor',
159     vendornumber   => 'VEND1704',
160   )->save;
161
162   $ar_chart        = SL::DB::Manager::Chart->find_by( accno => '1400' ); # Forderungen
163   $ap_chart        = SL::DB::Manager::Chart->find_by( accno => '1600' ); # Verbindlichkeiten
164   $bank            = SL::DB::Manager::Chart->find_by( accno => '1200' ); # Bank
165   $ar_amount_chart = SL::DB::Manager::Chart->find_by( accno => '8400' ); # Erlöse
166   $ap_amount_chart = SL::DB::Manager::Chart->find_by( accno => '3400' ); # Wareneingang 19%
167
168 }
169
170 sub test_ar_transaction {
171   my (%params) = @_;
172   my $netamount = $params{amount} || 100;
173   my $amount    = $::form->round_amount($netamount * 1.19,2);
174   my $invoice   = SL::DB::Invoice->new(
175       invoice      => 0,
176       invnumber    => $params{invnumber} || undef, # let it use its own invnumber
177       amount       => $amount,
178       netamount    => $netamount,
179       transdate    => $transdate1,
180       taxincluded  => $params{taxincluded } || 0,
181       customer_id  => $customer->id,
182       taxzone_id   => $customer->taxzone_id,
183       currency_id  => $currency_id,
184       transactions => [],
185       payment_id   => $params{payment_id} || undef,
186       notes        => 'test_ar_transaction',
187   );
188   $invoice->add_ar_amount_row(
189     amount => $invoice->netamount,
190     chart  => $ar_amount_chart,
191     tax_id => $params{tax_id} || $tax->id,
192   );
193
194   $invoice->create_ar_row(chart => $ar_chart);
195   $invoice->save;
196
197   is($invoice->currency_id , $currency_id , 'currency_id has been saved');
198   is($invoice->netamount   , $netamount   , 'ar amount has been converted');
199   is($invoice->amount      , $amount      , 'ar amount has been converted');
200   is($invoice->taxincluded , 0            , 'ar transaction doesn\'t have taxincluded');
201
202   if ( $netamount == 100 ) {
203     is(SL::DB::Manager::AccTransaction->find_by(chart_id => $ar_amount_chart->id , trans_id => $invoice->id)->amount , '100.00000'  , $ar_amount_chart->accno . ': has been converted for currency');
204     is(SL::DB::Manager::AccTransaction->find_by(chart_id => $ar_chart->id        , trans_id => $invoice->id)->amount , '-119.00000' , $ar_chart->accno . ': has been converted for currency');
205   }
206   return $invoice;
207 };
208
209 sub test_ap_transaction {
210   my (%params) = @_;
211   my $testname = 'test_ap_transaction';
212
213   my $netamount = 100;
214   my $amount    = $::form->round_amount($netamount * 1.19,2);
215   my $invoice   = SL::DB::PurchaseInvoice->new(
216     invoice      => 0,
217     invnumber    => $params{invnumber} || $testname,
218     amount       => $amount,
219     netamount    => $netamount,
220     transdate    => $transdate1,
221     taxincluded  => 0,
222     vendor_id    => $vendor->id,
223     taxzone_id   => $vendor->taxzone_id,
224     currency_id  => $currency_id,
225     transactions => [],
226     notes        => 'test_ap_transaction',
227   );
228   $invoice->add_ap_amount_row(
229     amount     => $invoice->netamount,
230     chart      => $ap_amount_chart,
231     tax_id     => $params{tax_id} || $tax_9->id,
232   );
233
234   $invoice->create_ap_row(chart => $ap_chart);
235   $invoice->save;
236
237   is($invoice->currency_id , $currency_id , "$testname: currency_id has been saved");
238   is($invoice->netamount   , 100          , "$testname: ap amount has been converted");
239   is($invoice->amount      , 119          , "$testname: ap amount has been converted");
240   is($invoice->taxincluded , 0            , "$testname: ap transaction doesn\'t have taxincluded");
241
242   is(SL::DB::Manager::AccTransaction->find_by(chart_id => $ap_amount_chart->id , trans_id => $invoice->id)->amount , '-100.00000' , $ap_amount_chart->accno . ': has been converted for currency');
243   is(SL::DB::Manager::AccTransaction->find_by(chart_id => $ap_chart->id        , trans_id => $invoice->id)->amount , '119.00000'  , $ap_chart->accno . ': has been converted for currency');
244
245   return $invoice;
246 };
247
248 ###### test cases
249
250 sub test1 {
251
252   my $testname = 'test1';
253
254   $ar_transaction = test_ar_transaction(invnumber => 'salesinv1');
255
256   my $bt = create_bank_transaction(record => $ar_transaction) or die "Couldn't create bank_transaction";
257
258   $::form->{invoice_ids} = {
259     $bt->id => [ $ar_transaction->id ]
260   };
261
262   save_btcontroller_to_string();
263
264   $ar_transaction->load;
265   $bt->load;
266   is($ar_transaction->paid   , '119.00000' , "$testname: salesinv1 was paid");
267   is($ar_transaction->closed , 1           , "$testname: salesinv1 is closed");
268   is($bt->invoice_amount     , '119.00000' , "$testname: bt invoice amount was assigned");
269
270 };
271
272 sub test_skonto_exact {
273
274   my $testname = 'test_skonto_exact';
275
276   $ar_transaction = test_ar_transaction(invnumber => 'salesinv skonto',
277                                         payment_id => $payment_terms->id,
278                                        );
279
280   my $bt = create_bank_transaction(record        => $ar_transaction,
281                                    bank_chart_id => $bank->id,
282                                    amount        => $ar_transaction->amount_less_skonto
283                                   ) or die "Couldn't create bank_transaction";
284
285   $::form->{invoice_ids} = {
286     $bt->id => [ $ar_transaction->id ]
287   };
288   $::form->{invoice_skontos} = {
289     $bt->id => [ 'with_skonto_pt' ]
290   };
291
292   save_btcontroller_to_string();
293
294   $ar_transaction->load;
295   $bt->load;
296   is($ar_transaction->paid   , '119.00000' , "$testname: salesinv skonto was paid");
297   is($ar_transaction->closed , 1           , "$testname: salesinv skonto is closed");
298   is($bt->invoice_amount     , '113.05000' , "$testname: bt invoice amount was assigned");
299
300 };
301
302 sub test_bt_error {
303
304   my $testname = 'test_rollback_error';
305   # without type with_free_skonto the helper function (Payment.pm) looks ugly but not
306   # breakable
307
308   $ar_transaction = test_ar_transaction(invnumber   => 'salesinv skonto',
309                                         payment_id  => $payment_terms->id,
310                                         taxincluded => 0,
311                                         amount      => 168.58 / 1.19,
312                                        );
313
314   my $bt = create_bank_transaction(record        => $ar_transaction,
315                                    bank_chart_id => $bank->id,
316                                    amount        => 160.15,
317                                   ) or die "Couldn't create bank_transaction";
318   $::form->{invoice_ids} = {
319     $bt->id => [ $ar_transaction->id ]
320   };
321   $::form->{invoice_skontos} = {
322     $bt->id => [ 'with_skonto_pt' ]
323   };
324
325   is($ar_transaction->paid   , '0' , "$testname: salesinv is not paid");
326
327   # generate an error for testing rollback mechanism
328   my $saved_skonto_sales_chart_id = $tax->skonto_sales_chart_id;
329   $tax->skonto_sales_chart_id(undef);
330   $tax->save;
331
332   save_btcontroller_to_string();
333   my @bt_errors = @{ $bt_controller->problems };
334   is(substr($bt_errors[0]->{message},0,38), 'Kein Skontokonto für Steuerschlüssel 3', "$testname: Fehlermeldung ok");
335   # set original value
336   $tax->skonto_sales_chart_id($saved_skonto_sales_chart_id);
337   $tax->save;
338
339   $ar_transaction->load;
340   $bt->load;
341   is($ar_transaction->paid   , '0.00000' , "$testname: salesinv was not paid");
342   is($bt->invoice_amount     , '0.00000' , "$testname: bt invoice amount was not assigned");
343
344 };
345
346 sub test_two_invoices {
347
348   my $testname = 'test_two_invoices';
349
350   my $ar_transaction_1 = test_ar_transaction(invnumber => 'salesinv_1');
351   my $ar_transaction_2 = test_ar_transaction(invnumber => 'salesinv_2');
352
353   my $bt = create_bank_transaction(record        => $ar_transaction_1,
354                                    amount        => ($ar_transaction_1->amount + $ar_transaction_2->amount),
355                                    purpose       => "Rechnungen " . $ar_transaction_1->invnumber . " und " . $ar_transaction_2->invnumber,
356                                    bank_chart_id => $bank->id,
357                                   ) or die "Couldn't create bank_transaction";
358
359   my ($agreement, $rule_matches) = $bt->get_agreement_with_invoice($ar_transaction_1);
360   is($agreement, 16, "points for ar_transaction_1 in test_two_invoices ok");
361
362   $::form->{invoice_ids} = {
363     $bt->id => [ $ar_transaction_1->id, $ar_transaction_2->id ]
364   };
365
366   save_btcontroller_to_string();
367
368   $ar_transaction_1->load;
369   $ar_transaction_2->load;
370   $bt->load;
371
372   is($ar_transaction_1->paid   , '119.00000' , "$testname: salesinv_1 wcsv_import_reportsas paid");
373   is($ar_transaction_1->closed , 1           , "$testname: salesinv_1 is closed");
374   is($ar_transaction_2->paid   , '119.00000' , "$testname: salesinv_2 was paid");
375   is($ar_transaction_2->closed , 1           , "$testname: salesinv_2 is closed");
376   is($bt->invoice_amount       , '238.00000' , "$testname: bt invoice amount was assigned");
377
378 }
379
380 sub test_one_inv_and_two_invoices_with_skonto_exact {
381
382   my $testname = 'test_two_invoices_with_skonto_exact';
383
384   my $ar_transaction_1 = test_ar_transaction(invnumber => 'salesinv 1 skonto',
385                                              payment_id => $payment_terms->id,
386                                             );
387   my $ar_transaction_2 = test_ar_transaction(invnumber => 'salesinv 2 skonto',
388                                              payment_id => $payment_terms->id,
389                                             );
390   my $ar_transaction_3 = test_ar_transaction(invnumber => 'salesinv 3 no skonto');
391
392
393
394   my $bt = create_bank_transaction(record        => $ar_transaction_1,
395                                    bank_chart_id => $bank->id,
396                                    amount        => $ar_transaction_1->amount_less_skonto * 2 + $ar_transaction_3->amount
397                                   ) or die "Couldn't create bank_transaction";
398
399   $::form->{invoice_ids} = {
400     $bt->id => [ $ar_transaction_1->id, $ar_transaction_3->id, $ar_transaction_2->id]
401   };
402   $::form->{invoice_skontos} = {
403     $bt->id => [ 'with_skonto_pt', 'without_skonto', 'with_skonto_pt' ]
404   };
405
406   save_btcontroller_to_string();
407
408   $ar_transaction_1->load;
409   $ar_transaction_2->load;
410   $ar_transaction_3->load;
411   my $skonto_1 = SL::DB::Manager::AccTransaction->find_by(trans_id => $ar_transaction_1->id, chart_id => 162);
412   my $skonto_2 = SL::DB::Manager::AccTransaction->find_by(trans_id => $ar_transaction_2->id, chart_id => 162);
413   $bt->load;
414   is($skonto_1->amount   , '-5.95000' , "$testname: salesinv 1 skonto was booked");
415   is($skonto_2->amount   , '-5.95000' , "$testname: salesinv 2 skonto was booked");
416   is($ar_transaction_1->paid   , '119.00000' , "$testname: salesinv 1 was paid");
417   is($ar_transaction_2->paid   , '119.00000' , "$testname: salesinv 2 was paid");
418   is($ar_transaction_3->paid   , '119.00000' , "$testname: salesinv 3 was paid");
419   is($ar_transaction_1->closed , 1           , "$testname: salesinv 1 skonto is closed");
420   is($ar_transaction_2->closed , 1           , "$testname: salesinv 2 skonto is closed");
421   is($ar_transaction_3->closed , 1           , "$testname: salesinv 2 skonto is closed");
422   is($bt->invoice_amount     , '345.10000' , "$testname: bt invoice amount was assigned");
423
424 }
425
426 sub test_overpayment {
427
428   my $testname = 'test_overpayment';
429
430   $ar_transaction = test_ar_transaction(invnumber => 'salesinv overpaid');
431
432   # amount 135 > 119
433   my $bt = create_bank_transaction(record        => $ar_transaction,
434                                    bank_chart_id => $bank->id,
435                                    amount        => 135
436                                   ) or die "Couldn't create bank_transaction";
437
438   $::form->{invoice_ids} = {
439     $bt->id => [ $ar_transaction->id ]
440   };
441
442   save_btcontroller_to_string();
443
444   $ar_transaction->load;
445   $bt->load;
446
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");
449 { local $TODO = 'this currently fails because closed ignores over-payments, see commit d90966c7';
450   is($ar_transaction->closed                   , 0           , "$testname: 'salesinv overpaid' is open (via 'closed' method')");
451 }
452   is($ar_transaction->open_amount == 0 ? 1 : 0 , 1           , "$testname: 'salesinv overpaid is closed (via amount-paid)");
453
454 };
455
456 sub test_overpayment_with_partialpayment {
457
458   # two payments on different days, 10 and 119. If there is only one invoice we
459   # don't want it to be overpaid.
460   my $testname = 'test_overpayment_with_partialpayment';
461
462   $ar_transaction = test_ar_transaction(invnumber => 'salesinv overpaid partial');
463
464   my $bt_1 = create_bank_transaction(record        => $ar_transaction,
465                                      bank_chart_id => $bank->id,
466                                      amount        =>  10
467                                     ) or die "Couldn't create bank_transaction";
468   my $bt_2 = create_bank_transaction(record        => $ar_transaction,
469                                      amount        => 119,
470                                      transdate     => DateTime->today->add(days => 5),
471                                      bank_chart_id => $bank->id,
472                                     ) or die "Couldn't create bank_transaction";
473
474   $::form->{invoice_ids} = {
475     $bt_1->id => [ $ar_transaction->id ]
476   };
477   save_btcontroller_to_string();
478
479   $bt_1->load;
480   is($bt_1->invoice_amount ,  '10.00000' , "$testname: bt_1 invoice amount was fully assigned");
481   $::form->{invoice_ids} = {
482     $bt_2->id => [ $ar_transaction->id ]
483   };
484   save_btcontroller_to_string();
485
486   $ar_transaction->load;
487   $bt_2->load;
488
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");
492
493 };
494
495 sub test_partial_payment {
496
497   my $testname = 'test_partial_payment';
498
499   $ar_transaction = test_ar_transaction(invnumber => 'salesinv partial payment');
500
501   # amount 100 < 119
502   my $bt = create_bank_transaction(record        => $ar_transaction,
503                                    bank_chart_id => $bank->id,
504                                    amount        => 100
505                                   ) or die "Couldn't create bank_transaction";
506
507   $::form->{invoice_ids} = {
508     $bt->id => [ $ar_transaction->id ]
509   };
510
511   save_btcontroller_to_string();
512
513   $ar_transaction->load;
514   $bt->load;
515
516   is($ar_transaction->paid , '100.00000' , "$testname: 'salesinv partial payment' was partially paid");
517   is($bt->invoice_amount   , '100.00000' , "$testname: bt invoice amount was assigned partially paid amount");
518
519 };
520
521 sub test_credit_note {
522
523   my $testname = 'test_credit_note';
524
525   my $part1 = new_part(   partnumber => 'T4254')->save;
526   my $part2 = new_service(partnumber => 'Serv1')->save;
527   my $credit_note = create_credit_note(
528     invnumber    => 'cn 1',
529     customer     => $customer,
530     taxincluded  => 0,
531     invoiceitems => [ create_invoice_item(part => $part1, qty =>  3, sellprice => 70),
532                       create_invoice_item(part => $part2, qty => 10, sellprice => 50),
533                     ]
534   );
535   my $bt            = create_bank_transaction(record        => $credit_note,
536                                                                 amount        => $credit_note->amount,
537                                                                 bank_chart_id => $bank->id,
538                                                                 transdate     => DateTime->today->add(days => 10),
539                                                                );
540   my ($agreement, $rule_matches) = $bt->get_agreement_with_invoice($credit_note);
541   is($agreement, 13, "points for credit note ok");
542   is($rule_matches, 'remote_account_number(3) exact_amount(4) wrong_sign(-1) depositor_matches(2) remote_name(2) payment_within_30_days(1) datebonus14(2) ', "rules_matches for credit note ok");
543
544   $::form->{invoice_ids} = {
545     $bt->id => [ $credit_note->id ]
546   };
547
548   save_btcontroller_to_string();
549
550   $credit_note->load;
551   $bt->load;
552   is($credit_note->amount   , '-844.90000', "$testname: amount ok");
553   is($credit_note->netamount, '-710.00000', "$testname: netamount ok");
554   is($credit_note->paid     , '-844.90000', "$testname: paid ok");
555 }
556
557 sub test_neg_ap_transaction {
558   my (%params) = @_;
559   my $testname = 'test_neg_ap_transaction' . $params{invoice} ? ' invoice booking' : ' credit booking';
560   my $netamount = -20;
561   my $amount    = $::form->round_amount($netamount * 1.19,2);
562   my $invoice   = SL::DB::PurchaseInvoice->new(
563     invoice      => $params{invoice} // 0,
564     invnumber    => $params{invnumber} || 'test_neg_ap_transaction',
565     amount       => $amount,
566     netamount    => $netamount,
567     transdate    => $transdate1,
568     taxincluded  => 0,
569     vendor_id    => $vendor->id,
570     taxzone_id   => $vendor->taxzone_id,
571     currency_id  => $currency_id,
572     transactions => [],
573     notes        => 'test_neg_ap_transaction',
574   );
575   $invoice->add_ap_amount_row(
576     amount     => $invoice->netamount,
577     chart      => $ap_amount_chart,
578     tax_id     => $tax_9->id,
579   );
580
581   $invoice->create_ap_row(chart => $ap_chart);
582   $invoice->save;
583
584   is($invoice->netamount, -20  , "$testname: netamount ok");
585   is($invoice->amount   , -23.8, "$testname: amount ok");
586
587   my $bt            = create_bank_transaction(record        => $invoice,
588                                               amount        => $invoice->amount,
589                                               bank_chart_id => $bank->id,
590                                               transdate     => DateTime->today->add(days => 10),
591                                                                );
592
593   my ($agreement, $rule_matches) = $bt->get_agreement_with_invoice($invoice);
594   is($agreement, 15, "points for negative ap transaction ok");
595
596   $::form->{invoice_ids} = {
597     $bt->id => [ $invoice->id ]
598   };
599
600   save_btcontroller_to_string();
601
602   $invoice->load;
603   $bt->load;
604
605   is($invoice->amount   , '-23.80000', "$testname: amount ok");
606   is($invoice->netamount, '-20.00000', "$testname: netamount ok");
607   is($invoice->paid     , '-23.80000', "$testname: paid ok");
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");
610
611   return $invoice;
612 };
613 sub test_two_neg_ap_transaction {
614   my $testname='test_two_neg_ap_transaction';
615   my $netamount = -20;
616   my $amount    = $::form->round_amount($netamount * 1.19,2);
617   my $invoice   = SL::DB::PurchaseInvoice->new(
618     invoice      =>  0,
619     invnumber    => 'test_neg_ap_transaction',
620     amount       => $amount,
621     netamount    => $netamount,
622     transdate    => $transdate1,
623     taxincluded  => 0,
624     vendor_id    => $vendor->id,
625     taxzone_id   => $vendor->taxzone_id,
626     currency_id  => $currency_id,
627     transactions => [],
628     notes        => 'test_neg_ap_transaction',
629   );
630   $invoice->add_ap_amount_row(
631     amount     => $invoice->netamount,
632     chart      => $ap_amount_chart,
633     tax_id     => $tax_9->id,
634   );
635
636   $invoice->create_ap_row(chart => $ap_chart);
637   $invoice->save;
638
639   is($invoice->netamount, -20  , "$testname: netamount ok");
640   is($invoice->amount   , -23.8, "$testname: amount ok");
641
642   my $netamount_two = -1.14;
643   my $amount_two    = $::form->round_amount($netamount_two * 1.19,2);
644   my $invoice_two   = SL::DB::PurchaseInvoice->new(
645     invoice      => 0,
646     invnumber    => 'test_neg_ap_transaction_two',
647     amount       => $amount_two,
648     netamount    => $netamount_two,
649     transdate    => $transdate1,
650     taxincluded  => 0,
651     vendor_id    => $vendor->id,
652     taxzone_id   => $vendor->taxzone_id,
653     currency_id  => $currency_id,
654     transactions => [],
655     notes        => 'test_neg_ap_transaction_two',
656   );
657   $invoice_two->add_ap_amount_row(
658     amount     => $invoice_two->netamount,
659     chart      => $ap_amount_chart,
660     tax_id     => $tax_9->id,
661   );
662
663   $invoice_two->create_ap_row(chart => $ap_chart);
664   $invoice_two->save;
665
666   is($invoice_two->netamount, -1.14  , "$testname: netamount ok");
667   is($invoice_two->amount   , -1.36, "$testname: amount ok");
668
669
670   my $bt            = create_bank_transaction(record        => $invoice_two,
671                                               amount        => $invoice_two->amount + $invoice->amount,
672                                               bank_chart_id => $bank->id,
673                                               transdate     => DateTime->today->add(days => 10),
674                                                                );
675   # my ($agreement, $rule_matches) = $bt->get_agreement_with_invoice($invoice_two);
676   # is($agreement, 15, "points for negative ap transaction ok");
677
678   $::form->{invoice_ids} = {
679     $bt->id => [ $invoice->id, $invoice_two->id ]
680   };
681
682   save_btcontroller_to_string();
683
684   $invoice->load;
685   $invoice_two->load;
686   $bt->load;
687
688   is($invoice->amount   , '-23.80000', "$testname: first inv amount ok");
689   is($invoice->netamount, '-20.00000', "$testname: first inv netamount ok");
690   is($invoice->paid     , '-23.80000', "$testname: first inv paid ok");
691   is($invoice_two->amount   , '-1.36000', "$testname: second inv amount ok");
692   is($invoice_two->netamount, '-1.14000', "$testname: second inv netamount ok");
693   is($invoice_two->paid     , '-1.36000', "$testname: second inv paid ok");
694   is($bt->invoice_amount, '25.16000', "$testname: bt invoice amount for both invoices were assigned");
695
696
697   return ($invoice, $invoice_two);
698 };
699
700 sub test_ap_payment_transaction {
701   my (%params) = @_;
702   my $testname = 'test_ap_payment_transaction';
703   my $netamount = 115;
704   my $amount    = $::form->round_amount($netamount * 1.19,2);
705   my $invoice   = SL::DB::PurchaseInvoice->new(
706     invoice      => 0,
707     invnumber    => $params{invnumber} || $testname,
708     amount       => $amount,
709     netamount    => $netamount,
710     transdate    => $transdate1,
711     taxincluded  => 0,
712     vendor_id    => $vendor->id,
713     taxzone_id   => $vendor->taxzone_id,
714     currency_id  => $currency_id,
715     transactions => [],
716     notes        => $testname,
717   );
718   $invoice->add_ap_amount_row(
719     amount     => $invoice->netamount,
720     chart      => $ap_amount_chart,
721     tax_id     => $tax_9->id,
722   );
723
724   $invoice->create_ap_row(chart => $ap_chart);
725   $invoice->save;
726
727   is($invoice->netamount, 115  , "$testname: netamount ok");
728   is($invoice->amount   , 136.85, "$testname: amount ok");
729
730   my $bt            = create_bank_transaction(record        => $invoice,
731                                               amount        => $invoice->amount,
732                                               bank_chart_id => $bank->id,
733                                               transdate     => DateTime->today->add(days => 10),
734                                              );
735   $::form->{invoice_ids} = {
736     $bt->id => [ $invoice->id ]
737   };
738
739   save_btcontroller_to_string();
740
741   $invoice->load;
742   $bt->load;
743
744   is($invoice->amount   , '136.85000', "$testname: amount ok");
745   is($invoice->netamount, '115.00000', "$testname: netamount ok");
746   is($bt->amount, '-136.85000', "$testname: bt amount ok");
747   is($invoice->paid     , '136.85000', "$testname: paid ok");
748   is($bt->invoice_amount, '-136.85000', "$testname: bt invoice amount for ap was assigned");
749
750   return $invoice;
751 };
752
753 sub test_ap_payment_part_transaction {
754   my (%params) = @_;
755   my $testname = 'test_ap_payment_p_transaction';
756   my $netamount = 115;
757   my $amount    = $::form->round_amount($netamount * 1.19,2);
758   my $invoice   = SL::DB::PurchaseInvoice->new(
759     invoice      => 0,
760     invnumber    => $params{invnumber} || $testname,
761     amount       => $amount,
762     netamount    => $netamount,
763     transdate    => $transdate1,
764     taxincluded  => 0,
765     vendor_id    => $vendor->id,
766     taxzone_id   => $vendor->taxzone_id,
767     currency_id  => $currency_id,
768     transactions => [],
769     notes        => $testname,
770   );
771   $invoice->add_ap_amount_row(
772     amount     => $invoice->netamount,
773     chart      => $ap_amount_chart,
774     tax_id     => $tax_9->id,
775   );
776
777   $invoice->create_ap_row(chart => $ap_chart);
778   $invoice->save;
779
780   is($invoice->netamount, 115  , "$testname: netamount ok");
781   is($invoice->amount   , 136.85, "$testname: amount ok");
782
783   my $bt            = create_bank_transaction(record        => $invoice,
784                                               amount        => $invoice->amount-100,
785                                               bank_chart_id => $bank->id,
786                                               transdate     => DateTime->today->add(days => 10),
787                                              );
788   $::form->{invoice_ids} = {
789     $bt->id => [ $invoice->id ]
790   };
791
792   save_btcontroller_to_string();
793
794   $invoice->load;
795   $bt->load;
796
797   is($invoice->amount   , '136.85000', "$testname: amount ok");
798   is($invoice->netamount, '115.00000', "$testname: netamount ok");
799   is($bt->amount,         '-36.85000', "$testname: bt amount ok");
800   is($invoice->paid     ,  '36.85000', "$testname: paid ok");
801   is($bt->invoice_amount, '-36.85000', "$testname: bt invoice amount for ap was assigned");
802
803   my $bt2           = create_bank_transaction(record        => $invoice,
804                                               amount        => 100,
805                                               bank_chart_id => $bank->id,
806                                               transdate     => DateTime->today->add(days => 10),
807                                              );
808   $::form->{invoice_ids} = {
809     $bt2->id => [ $invoice->id ]
810   };
811
812   save_btcontroller_to_string();
813   $invoice->load;
814   $bt2->load;
815
816   is($invoice->amount   , '136.85000', "$testname: amount ok");
817   is($invoice->netamount, '115.00000', "$testname: netamount ok");
818   is($bt2->amount,        '-100.00000',"$testname: bt amount ok");
819   is($invoice->paid     , '136.85000', "$testname: paid ok");
820   is($bt2->invoice_amount,'-100.00000', "$testname: bt invoice amount for ap was assigned");
821
822   return $invoice;
823 };
824
825 sub test_neg_sales_invoice {
826
827   my $testname = 'test_neg_sales_invoice';
828
829   my $part1 = new_part(   partnumber => 'Funkenhaube öhm')->save;
830   my $part2 = new_service(partnumber => 'Service-Pauschale Pasch!')->save;
831
832   my $neg_sales_inv = create_sales_invoice(
833     invnumber    => '20172201',
834     customer     => $customer,
835     taxincluded  => 0,
836     invoiceitems => [ create_invoice_item(part => $part1, qty =>  3, sellprice => 70),
837                       create_invoice_item(part => $part2, qty => 10, sellprice => -50),
838                     ]
839   );
840   my $bt            = create_bank_transaction(record        => $neg_sales_inv,
841                                                                 amount        => $neg_sales_inv->amount,
842                                                                 bank_chart_id => $bank->id,
843                                                                 transdate     => DateTime->today,
844                                                                );
845   $::form->{invoice_ids} = {
846     $bt->id => [ $neg_sales_inv->id ]
847   };
848
849   save_btcontroller_to_string();
850
851   $neg_sales_inv->load;
852   $bt->load;
853   is($neg_sales_inv->amount   , '-345.10000', "$testname: amount ok");
854   is($neg_sales_inv->netamount, '-290.00000', "$testname: netamount ok");
855   is($neg_sales_inv->paid     , '-345.10000', "$testname: paid ok");
856   is($bt->amount              , '-345.10000', "$testname: bt amount ok");
857   is($bt->invoice_amount      , '-345.10000', "$testname: bt invoice_amount ok");
858 }
859
860 sub test_bt_rule1 {
861
862   my $testname = 'test_bt_rule1';
863
864   $ar_transaction = test_ar_transaction(invnumber => 'bt_rule1');
865
866   my $bt = create_bank_transaction(record => $ar_transaction) or die "Couldn't create bank_transaction";
867
868   $ar_transaction->load;
869   $bt->load;
870   is($ar_transaction->paid   , '0.00000' , "$testname: not paid");
871   is($bt->invoice_amount     , '0.00000' , "$testname: bt invoice amount was not assigned");
872
873   my $bt_controller = SL::Controller::BankTransaction->new;
874   $::form->{dont_render_for_test} = 1;
875   $::form->{filter}{bank_account} = $bank_account->id;
876   my ( $bt_transactions, $proposals ) = $bt_controller->action_list;
877
878   is(scalar(@$bt_transactions)         , 1  , "$testname: one bank_transaction");
879   is($bt_transactions->[0]->{agreement}, 20 , "$testname: agreement == 20");
880   my $match = join ( ' ',@{$bt_transactions->[0]->{rule_matches}});
881   #print "rule_matches='".$match."'\n";
882   is($match,
883      "remote_account_number(3) exact_amount(4) own_invoice_in_purpose(5) depositor_matches(2) remote_name(2) payment_within_30_days(1) datebonus0(3) ",
884      "$testname: rule_matches ok");
885   $bt->invoice_amount($bt->amount);
886   $bt->save;
887   is($bt->invoice_amount     , '119.00000' , "$testname: bt invoice amount now set");
888 };
889
890 sub test_sepa_export {
891
892   my $testname = 'test_sepa_export';
893
894   $ar_transaction = test_ar_transaction(invnumber => 'sepa1');
895
896   my $bt  = create_bank_transaction(record => $ar_transaction) or die "Couldn't create bank_transaction";
897   my $se  = create_sepa_export();
898   my $sei = create_sepa_export_item(
899     chart_id       => $bank->id,
900     ar_id          => $ar_transaction->id,
901     sepa_export_id => $se->id,
902     vc_iban        => $customer->iban,
903     vc_bic         => $customer->bic,
904     vc_mandator_id => $customer->mandator_id,
905     vc_depositor   => $customer->depositor,
906     amount         => $ar_transaction->amount,
907   );
908   require SL::SEPA::XML;
909   my $sepa_xml   = SL::SEPA::XML->new('company'     => $customer->name,
910                                       'creditor_id' => "id",
911                                       'src_charset' => 'UTF-8',
912                                       'message_id'  => "test",
913                                       'grouped'     => 1,
914                                       'collection'  => 1,
915                                      );
916   is($sepa_xml->{company}    , 'Test Customer OLE S.L. Ardbaerg AB');
917
918   $ar_transaction->load;
919   $bt->load;
920   $sei->load;
921   is($ar_transaction->paid   , '0.00000' , "$testname: sepa1 not paid");
922   is($bt->invoice_amount     , '0.00000' , "$testname: bt invoice amount was not assigned");
923   is($bt->amount             , '119.00000' , "$testname: bt amount ok");
924   is($sei->amount            , '119.00000' , "$testname: sepa export amount ok");
925
926   my $bt_controller = SL::Controller::BankTransaction->new;
927   $::form->{dont_render_for_test} = 1;
928   $::form->{filter}{bank_account} = $bank_account->id;
929   my ( $bt_transactions, $proposals ) = $bt_controller->action_list;
930
931   is(scalar(@$bt_transactions)         , 1  , "$testname: one bank_transaction");
932   is($bt_transactions->[0]->{agreement}, 25 , "$testname: agreement == 25");
933   my $match = join ( ' ',@{$bt_transactions->[0]->{rule_matches}});
934   is($match,
935      "remote_account_number(3) exact_amount(4) own_invoice_in_purpose(5) depositor_matches(2) remote_name(2) payment_within_30_days(1) datebonus0(3) sepa_export_item(5) ",
936      "$testname: rule_matches ok");
937 };
938
939 sub test_two_banktransactions {
940
941   my $testname = 'two_banktransactions';
942
943   my $ar_transaction_1 = test_ar_transaction(invnumber => 'salesinv10000' , amount => 2912.00 );
944   my $bt1 = create_bank_transaction(record        => $ar_transaction_1,
945                                     amount        => $ar_transaction_1->amount,
946                                     purpose       => "Rechnung10000 beinahe",
947                                     bank_chart_id => $bank->id,
948                                   ) or die "Couldn't create bank_transaction";
949
950   my $bt2 = create_bank_transaction(record        => $ar_transaction_1,
951                                     amount        => $ar_transaction_1->amount + 0.01,
952                                     purpose       => "sicher salesinv20000 vielleicht",
953                                     bank_chart_id => $bank->id,
954                                   ) or die "Couldn't create bank_transaction";
955
956   my ($agreement1, $rule_matches1) = $bt1->get_agreement_with_invoice($ar_transaction_1);
957   is($agreement1, 19, "bt1 19 points for ar_transaction_1 in $testname ok");
958   #print "rule_matches1=".$rule_matches1."\n";
959   is($rule_matches1,
960      "remote_account_number(3) exact_amount(4) own_invnumber_in_purpose(4) depositor_matches(2) remote_name(2) payment_within_30_days(1) datebonus0(3) ",
961      "$testname: rule_matches ok");
962   my ($agreement2, $rule_matches2) = $bt2->get_agreement_with_invoice($ar_transaction_1);
963   is($agreement2, 11, "bt2 11 points for ar_transaction_1 in $testname ok");
964   is($rule_matches2,
965      "remote_account_number(3) depositor_matches(2) remote_name(2) payment_within_30_days(1) datebonus0(3) ",
966      "$testname: rule_matches ok");
967
968   my $ar_transaction_2 = test_ar_transaction(invnumber => 'salesinv20000' , amount => 2912.01 );
969   my $ar_transaction_3 = test_ar_transaction(invnumber => 'zweitemit10000', amount => 2912.00 );
970      ($agreement1, $rule_matches1) = $bt1->get_agreement_with_invoice($ar_transaction_2);
971
972   is($agreement1, 11, "bt1 11 points for ar_transaction_2 in $testname ok");
973
974      ($agreement2, $rule_matches2) = $bt2->get_agreement_with_invoice($ar_transaction_2);
975   is($agreement2, 20, "bt2 20 points for ar_transaction_2 in $testname ok");
976
977      ($agreement2, $rule_matches2) = $bt2->get_agreement_with_invoice($ar_transaction_1);
978   is($agreement2, 11, "bt2 11 points for ar_transaction_1 in $testname ok");
979
980   my $bt3 = create_bank_transaction(record        => $ar_transaction_3,
981                                     amount        => $ar_transaction_3->amount,
982                                     purpose       => "sicher Rechnung10000 vielleicht",
983                                     bank_chart_id => $bank->id,
984                                   ) or die "Couldn't create bank_transaction";
985
986   my ($agreement3, $rule_matches3) = $bt3->get_agreement_with_invoice($ar_transaction_3);
987   is($agreement3, 19, "bt3 19 points for ar_transaction_3 in $testname ok");
988
989   $bt2->delete;
990   $ar_transaction_2->delete;
991
992   #nun sollten zwei gleichwertige Rechnungen $ar_transaction_1 und $ar_transaction_3 für $bt1 gefunden werden
993   #aber es darf keine Proposals geben mit mehreren Rechnungen
994   my $bt_controller = SL::Controller::BankTransaction->new;
995   $::form->{dont_render_for_test} = 1;
996   $::form->{filter}{bank_account} = $bank_account->id;
997   my ( $bt_transactions, $proposals ) = $bt_controller->action_list;
998
999   is(scalar(@$bt_transactions)   , 2  , "$testname: two bank_transaction");
1000   is(scalar(@$proposals)         , 0  , "$testname: no proposals");
1001
1002   $ar_transaction_3->delete;
1003
1004   # Jetzt gibt es zwei Kontobewegungen mit gleichen Punkten für eine Rechnung.
1005   # hier darf es auch keine Proposals geben
1006
1007   ( $bt_transactions, $proposals ) = $bt_controller->action_list;
1008
1009   is(scalar(@$bt_transactions)   , 2  , "$testname: two bank_transaction");
1010   # odyn testfall - anforderungen so (noch) nicht in kivi
1011   # is(scalar(@$proposals)         , 0  , "$testname: no proposals");
1012
1013   # Jetzt gibt es zwei Kontobewegungen für eine Rechnung.
1014   # eine Bewegung bekommt mehr Punkte
1015   # hier darf es auch keine Proposals geben
1016   $bt3->update_attributes( purpose => "fuer Rechnung salesinv10000");
1017
1018   ( $bt_transactions, $proposals ) = $bt_controller->action_list;
1019
1020   is(scalar(@$bt_transactions)   , 2  , "$testname: two bank_transaction");
1021   # odyn testfall - anforderungen so (noch) nicht in kivi
1022   # is(scalar(@$proposals)         , 1  , "$testname: one proposal");
1023
1024 };
1025
1026
1027 1;