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