Bankimport: Fehler beim Verbuchen von Teilzahlungen: Rollback bei Fehler
[kivitendo-erp.git] / t / bank / bank_transactions.t
1 use Test::More tests => 197;
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_free_skonto';
303
304   $ar_transaction = test_ar_transaction(invnumber   => 'salesinv skonto',
305                                         payment_id  => $payment_terms->id,
306                                         taxincluded => 0,
307                                         amount      => 168.58 / 1.19,
308                                        );
309
310   my $bt = create_bank_transaction(record        => $ar_transaction,
311                                    bank_chart_id => $bank->id,
312                                    amount        => 158.58,
313                                   ) or die "Couldn't create bank_transaction";
314
315   $::form->{invoice_ids} = {
316     $bt->id => [ $ar_transaction->id ]
317   };
318   $::form->{invoice_skontos} = {
319     $bt->id => [ 'with_free_skonto' ]
320   };
321
322   is($ar_transaction->paid   , '0' , "$testname: salesinv is not paid");
323
324   # generate an error for testing rollback mechanism
325   my $saved_skonto_sales_chart_id = $tax->skonto_sales_chart_id;
326   $tax->skonto_sales_chart_id(undef);
327   $tax->save;
328
329   save_btcontroller_to_string();
330   my @bt_errors = @{ $bt_controller->problems };
331   is(substr($bt_errors[0]->{message},0,38), 'Kein Skontokonto für Steuerschlüssel 3', "$testname: Fehlermeldung ok");
332   # set original value
333   $tax->skonto_sales_chart_id($saved_skonto_sales_chart_id);
334   $tax->save;
335
336   $ar_transaction->load;
337   $bt->load;
338   is($ar_transaction->paid   , '0.00000' , "$testname: salesinv was not paid");
339   is($bt->invoice_amount     , '0.00000' , "$testname: bt invoice amount was not assigned");
340
341 };
342
343 sub test_two_invoices {
344
345   my $testname = 'test_two_invoices';
346
347   my $ar_transaction_1 = test_ar_transaction(invnumber => 'salesinv_1');
348   my $ar_transaction_2 = test_ar_transaction(invnumber => 'salesinv_2');
349
350   my $bt = create_bank_transaction(record        => $ar_transaction_1,
351                                    amount        => ($ar_transaction_1->amount + $ar_transaction_2->amount),
352                                    purpose       => "Rechnungen " . $ar_transaction_1->invnumber . " und " . $ar_transaction_2->invnumber,
353                                    bank_chart_id => $bank->id,
354                                   ) or die "Couldn't create bank_transaction";
355
356   my ($agreement, $rule_matches) = $bt->get_agreement_with_invoice($ar_transaction_1);
357   is($agreement, 16, "points for ar_transaction_1 in test_two_invoices ok");
358
359   $::form->{invoice_ids} = {
360     $bt->id => [ $ar_transaction_1->id, $ar_transaction_2->id ]
361   };
362
363   save_btcontroller_to_string();
364
365   $ar_transaction_1->load;
366   $ar_transaction_2->load;
367   $bt->load;
368
369   is($ar_transaction_1->paid   , '119.00000' , "$testname: salesinv_1 wcsv_import_reportsas paid");
370   is($ar_transaction_1->closed , 1           , "$testname: salesinv_1 is closed");
371   is($ar_transaction_2->paid   , '119.00000' , "$testname: salesinv_2 was paid");
372   is($ar_transaction_2->closed , 1           , "$testname: salesinv_2 is closed");
373   is($bt->invoice_amount       , '238.00000' , "$testname: bt invoice amount was assigned");
374
375 }
376
377 sub test_one_inv_and_two_invoices_with_skonto_exact {
378
379   my $testname = 'test_two_invoices_with_skonto_exact';
380
381   my $ar_transaction_1 = test_ar_transaction(invnumber => 'salesinv 1 skonto',
382                                              payment_id => $payment_terms->id,
383                                             );
384   my $ar_transaction_2 = test_ar_transaction(invnumber => 'salesinv 2 skonto',
385                                              payment_id => $payment_terms->id,
386                                             );
387   my $ar_transaction_3 = test_ar_transaction(invnumber => 'salesinv 3 no skonto');
388
389
390
391   my $bt = create_bank_transaction(record        => $ar_transaction_1,
392                                    bank_chart_id => $bank->id,
393                                    amount        => $ar_transaction_1->amount_less_skonto * 2 + $ar_transaction_3->amount
394                                   ) or die "Couldn't create bank_transaction";
395
396   $::form->{invoice_ids} = {
397     $bt->id => [ $ar_transaction_1->id, $ar_transaction_3->id, $ar_transaction_2->id]
398   };
399   $::form->{invoice_skontos} = {
400     $bt->id => [ 'with_skonto_pt', 'without_skonto', 'with_skonto_pt' ]
401   };
402
403   save_btcontroller_to_string();
404
405   $ar_transaction_1->load;
406   $ar_transaction_2->load;
407   $ar_transaction_3->load;
408   my $skonto_1 = SL::DB::Manager::AccTransaction->find_by(trans_id => $ar_transaction_1->id, chart_id => 162);
409   my $skonto_2 = SL::DB::Manager::AccTransaction->find_by(trans_id => $ar_transaction_2->id, chart_id => 162);
410   $bt->load;
411   is($skonto_1->amount   , '-5.95000' , "$testname: salesinv 1 skonto was booked");
412   is($skonto_2->amount   , '-5.95000' , "$testname: salesinv 2 skonto was booked");
413   is($ar_transaction_1->paid   , '119.00000' , "$testname: salesinv 1 was paid");
414   is($ar_transaction_2->paid   , '119.00000' , "$testname: salesinv 2 was paid");
415   is($ar_transaction_3->paid   , '119.00000' , "$testname: salesinv 3 was paid");
416   is($ar_transaction_1->closed , 1           , "$testname: salesinv 1 skonto is closed");
417   is($ar_transaction_2->closed , 1           , "$testname: salesinv 2 skonto is closed");
418   is($ar_transaction_3->closed , 1           , "$testname: salesinv 2 skonto is closed");
419   is($bt->invoice_amount     , '345.10000' , "$testname: bt invoice amount was assigned");
420
421 }
422
423 sub test_overpayment {
424
425   my $testname = 'test_overpayment';
426
427   $ar_transaction = test_ar_transaction(invnumber => 'salesinv overpaid');
428
429   # amount 135 > 119
430   my $bt = create_bank_transaction(record        => $ar_transaction,
431                                    bank_chart_id => $bank->id,
432                                    amount        => 135
433                                   ) or die "Couldn't create bank_transaction";
434
435   $::form->{invoice_ids} = {
436     $bt->id => [ $ar_transaction->id ]
437   };
438
439   save_btcontroller_to_string();
440
441   $ar_transaction->load;
442   $bt->load;
443
444   is($ar_transaction->paid                     , '135.00000' , "$testname: 'salesinv overpaid' was overpaid");
445   is($bt->invoice_amount                       , '135.00000' , "$testname: bt invoice amount was assigned overpaid amount");
446 { local $TODO = 'this currently fails because closed ignores over-payments, see commit d90966c7';
447   is($ar_transaction->closed                   , 0           , "$testname: 'salesinv overpaid' is open (via 'closed' method')");
448 }
449   is($ar_transaction->open_amount == 0 ? 1 : 0 , 0           , "$testname: 'salesinv overpaid is open (via amount-paid)");
450
451 };
452
453 sub test_overpayment_with_partialpayment {
454
455   # two payments on different days, 10 and 119. If there is only one invoice we want it be overpaid.
456   my $testname = 'test_overpayment_with_partialpayment';
457
458   $ar_transaction = test_ar_transaction(invnumber => 'salesinv overpaid partial');
459
460   my $bt_1 = create_bank_transaction(record        => $ar_transaction,
461                                      bank_chart_id => $bank->id,
462                                      amount        =>  10
463                                     ) or die "Couldn't create bank_transaction";
464   my $bt_2 = create_bank_transaction(record        => $ar_transaction,
465                                      amount        => 119,
466                                      transdate     => DateTime->today->add(days => 5),
467                                      bank_chart_id => $bank->id,
468                                     ) or die "Couldn't create bank_transaction";
469
470   $::form->{invoice_ids} = {
471     $bt_1->id => [ $ar_transaction->id ]
472   };
473   save_btcontroller_to_string();
474
475   $::form->{invoice_ids} = {
476     $bt_2->id => [ $ar_transaction->id ]
477   };
478   save_btcontroller_to_string();
479
480   $ar_transaction->load;
481   $bt_1->load;
482   $bt_2->load;
483
484   is($ar_transaction->paid , '129.00000' , "$testname: 'salesinv overpaid partial' was overpaid");
485   is($bt_1->invoice_amount ,  '10.00000' , "$testname: bt_1 invoice amount was assigned overpaid amount");
486   is($bt_2->invoice_amount , '119.00000' , "$testname: bt_2 invoice amount was assigned overpaid amount");
487
488 };
489
490 sub test_partial_payment {
491
492   my $testname = 'test_partial_payment';
493
494   $ar_transaction = test_ar_transaction(invnumber => 'salesinv partial payment');
495
496   # amount 100 < 119
497   my $bt = create_bank_transaction(record        => $ar_transaction,
498                                    bank_chart_id => $bank->id,
499                                    amount        => 100
500                                   ) or die "Couldn't create bank_transaction";
501
502   $::form->{invoice_ids} = {
503     $bt->id => [ $ar_transaction->id ]
504   };
505
506   save_btcontroller_to_string();
507
508   $ar_transaction->load;
509   $bt->load;
510
511   is($ar_transaction->paid , '100.00000' , "$testname: 'salesinv partial payment' was partially paid");
512   is($bt->invoice_amount   , '100.00000' , "$testname: bt invoice amount was assigned partially paid amount");
513
514 };
515
516 sub test_credit_note {
517
518   my $testname = 'test_credit_note';
519
520   my $part1 = new_part(   partnumber => 'T4254')->save;
521   my $part2 = new_service(partnumber => 'Serv1')->save;
522   my $credit_note = create_credit_note(
523     invnumber    => 'cn 1',
524     customer     => $customer,
525     taxincluded  => 0,
526     invoiceitems => [ create_invoice_item(part => $part1, qty =>  3, sellprice => 70),
527                       create_invoice_item(part => $part2, qty => 10, sellprice => 50),
528                     ]
529   );
530   my $bt            = create_bank_transaction(record        => $credit_note,
531                                                                 amount        => $credit_note->amount,
532                                                                 bank_chart_id => $bank->id,
533                                                                 transdate     => DateTime->today->add(days => 10),
534                                                                );
535   my ($agreement, $rule_matches) = $bt->get_agreement_with_invoice($credit_note);
536   is($agreement, 13, "points for credit note ok");
537   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");
538
539   $::form->{invoice_ids} = {
540     $bt->id => [ $credit_note->id ]
541   };
542
543   save_btcontroller_to_string();
544
545   $credit_note->load;
546   $bt->load;
547   is($credit_note->amount   , '-844.90000', "$testname: amount ok");
548   is($credit_note->netamount, '-710.00000', "$testname: netamount ok");
549   is($credit_note->paid     , '-844.90000', "$testname: paid ok");
550 }
551
552 sub test_neg_ap_transaction {
553   my (%params) = @_;
554   my $testname = 'test_neg_ap_transaction' . $params{invoice} ? ' invoice booking' : ' credit booking';
555   my $netamount = -20;
556   my $amount    = $::form->round_amount($netamount * 1.19,2);
557   my $invoice   = SL::DB::PurchaseInvoice->new(
558     invoice      => $params{invoice} // 0,
559     invnumber    => $params{invnumber} || 'test_neg_ap_transaction',
560     amount       => $amount,
561     netamount    => $netamount,
562     transdate    => $transdate1,
563     taxincluded  => 0,
564     vendor_id    => $vendor->id,
565     taxzone_id   => $vendor->taxzone_id,
566     currency_id  => $currency_id,
567     transactions => [],
568     notes        => 'test_neg_ap_transaction',
569   );
570   $invoice->add_ap_amount_row(
571     amount     => $invoice->netamount,
572     chart      => $ap_amount_chart,
573     tax_id     => $tax_9->id,
574   );
575
576   $invoice->create_ap_row(chart => $ap_chart);
577   $invoice->save;
578
579   is($invoice->netamount, -20  , "$testname: netamount ok");
580   is($invoice->amount   , -23.8, "$testname: amount ok");
581
582   my $bt            = create_bank_transaction(record        => $invoice,
583                                               amount        => $invoice->amount,
584                                               bank_chart_id => $bank->id,
585                                               transdate     => DateTime->today->add(days => 10),
586                                                                );
587   my ($agreement, $rule_matches) = $bt->get_agreement_with_invoice($invoice);
588   is($agreement, 15, "points for negative ap transaction ok");
589
590   $::form->{invoice_ids} = {
591     $bt->id => [ $invoice->id ]
592   };
593
594   save_btcontroller_to_string();
595
596   $invoice->load;
597   $bt->load;
598
599   is($invoice->amount   , '-23.80000', "$testname: amount ok");
600   is($invoice->netamount, '-20.00000', "$testname: netamount ok");
601   is($invoice->paid     , '-23.80000', "$testname: paid ok");
602   is($bt->invoice_amount, '23.80000', "$testname: bt invoice amount for ap was assigned");
603
604   return $invoice;
605 };
606 sub test_two_neg_ap_transaction {
607   my $testname='test_two_neg_ap_transaction';
608   my $netamount = -20;
609   my $amount    = $::form->round_amount($netamount * 1.19,2);
610   my $invoice   = SL::DB::PurchaseInvoice->new(
611     invoice      =>  0,
612     invnumber    => 'test_neg_ap_transaction',
613     amount       => $amount,
614     netamount    => $netamount,
615     transdate    => $transdate1,
616     taxincluded  => 0,
617     vendor_id    => $vendor->id,
618     taxzone_id   => $vendor->taxzone_id,
619     currency_id  => $currency_id,
620     transactions => [],
621     notes        => 'test_neg_ap_transaction',
622   );
623   $invoice->add_ap_amount_row(
624     amount     => $invoice->netamount,
625     chart      => $ap_amount_chart,
626     tax_id     => $tax_9->id,
627   );
628
629   $invoice->create_ap_row(chart => $ap_chart);
630   $invoice->save;
631
632   is($invoice->netamount, -20  , "$testname: netamount ok");
633   is($invoice->amount   , -23.8, "$testname: amount ok");
634
635   my $netamount_two = -1.14;
636   my $amount_two    = $::form->round_amount($netamount_two * 1.19,2);
637   my $invoice_two   = SL::DB::PurchaseInvoice->new(
638     invoice      => 0,
639     invnumber    => 'test_neg_ap_transaction_two',
640     amount       => $amount_two,
641     netamount    => $netamount_two,
642     transdate    => $transdate1,
643     taxincluded  => 0,
644     vendor_id    => $vendor->id,
645     taxzone_id   => $vendor->taxzone_id,
646     currency_id  => $currency_id,
647     transactions => [],
648     notes        => 'test_neg_ap_transaction_two',
649   );
650   $invoice_two->add_ap_amount_row(
651     amount     => $invoice_two->netamount,
652     chart      => $ap_amount_chart,
653     tax_id     => $tax_9->id,
654   );
655
656   $invoice_two->create_ap_row(chart => $ap_chart);
657   $invoice_two->save;
658
659   is($invoice_two->netamount, -1.14  , "$testname: netamount ok");
660   is($invoice_two->amount   , -1.36, "$testname: amount ok");
661
662
663   my $bt            = create_bank_transaction(record        => $invoice_two,
664                                               amount        => $invoice_two->amount + $invoice->amount,
665                                               bank_chart_id => $bank->id,
666                                               transdate     => DateTime->today->add(days => 10),
667                                                                );
668   # my ($agreement, $rule_matches) = $bt->get_agreement_with_invoice($invoice_two);
669   # is($agreement, 15, "points for negative ap transaction ok");
670
671   $::form->{invoice_ids} = {
672     $bt->id => [ $invoice->id, $invoice_two->id ]
673   };
674
675   save_btcontroller_to_string();
676
677   $invoice->load;
678   $invoice_two->load;
679   $bt->load;
680
681   is($invoice->amount   , '-23.80000', "$testname: first inv amount ok");
682   is($invoice->netamount, '-20.00000', "$testname: first inv netamount ok");
683   is($invoice->paid     , '-23.80000', "$testname: first inv paid ok");
684   is($invoice_two->amount   , '-1.36000', "$testname: second inv amount ok");
685   is($invoice_two->netamount, '-1.14000', "$testname: second inv netamount ok");
686   is($invoice_two->paid     , '-1.36000', "$testname: second inv paid ok");
687   is($bt->invoice_amount, '25.16000', "$testname: bt invoice amount for both invoices were assigned");
688
689
690   return ($invoice, $invoice_two);
691 };
692
693 sub test_ap_payment_transaction {
694   my (%params) = @_;
695   my $testname = 'test_ap_payment_transaction';
696   my $netamount = 115;
697   my $amount    = $::form->round_amount($netamount * 1.19,2);
698   my $invoice   = SL::DB::PurchaseInvoice->new(
699     invoice      => 0,
700     invnumber    => $params{invnumber} || $testname,
701     amount       => $amount,
702     netamount    => $netamount,
703     transdate    => $transdate1,
704     taxincluded  => 0,
705     vendor_id    => $vendor->id,
706     taxzone_id   => $vendor->taxzone_id,
707     currency_id  => $currency_id,
708     transactions => [],
709     notes        => $testname,
710   );
711   $invoice->add_ap_amount_row(
712     amount     => $invoice->netamount,
713     chart      => $ap_amount_chart,
714     tax_id     => $tax_9->id,
715   );
716
717   $invoice->create_ap_row(chart => $ap_chart);
718   $invoice->save;
719
720   is($invoice->netamount, 115  , "$testname: netamount ok");
721   is($invoice->amount   , 136.85, "$testname: amount ok");
722
723   my $bt            = create_bank_transaction(record        => $invoice,
724                                               amount        => $invoice->amount,
725                                               bank_chart_id => $bank->id,
726                                               transdate     => DateTime->today->add(days => 10),
727                                              );
728   $::form->{invoice_ids} = {
729     $bt->id => [ $invoice->id ]
730   };
731
732   save_btcontroller_to_string();
733
734   $invoice->load;
735   $bt->load;
736
737   is($invoice->amount   , '136.85000', "$testname: amount ok");
738   is($invoice->netamount, '115.00000', "$testname: netamount ok");
739   is($bt->amount, '-136.85000', "$testname: bt amount ok");
740   is($invoice->paid     , '136.85000', "$testname: paid ok");
741   is($bt->invoice_amount, '-136.85000', "$testname: bt invoice amount for ap was assigned");
742
743   return $invoice;
744 };
745
746 sub test_ap_payment_part_transaction {
747   my (%params) = @_;
748   my $testname = 'test_ap_payment_p_transaction';
749   my $netamount = 115;
750   my $amount    = $::form->round_amount($netamount * 1.19,2);
751   my $invoice   = SL::DB::PurchaseInvoice->new(
752     invoice      => 0,
753     invnumber    => $params{invnumber} || $testname,
754     amount       => $amount,
755     netamount    => $netamount,
756     transdate    => $transdate1,
757     taxincluded  => 0,
758     vendor_id    => $vendor->id,
759     taxzone_id   => $vendor->taxzone_id,
760     currency_id  => $currency_id,
761     transactions => [],
762     notes        => $testname,
763   );
764   $invoice->add_ap_amount_row(
765     amount     => $invoice->netamount,
766     chart      => $ap_amount_chart,
767     tax_id     => $tax_9->id,
768   );
769
770   $invoice->create_ap_row(chart => $ap_chart);
771   $invoice->save;
772
773   is($invoice->netamount, 115  , "$testname: netamount ok");
774   is($invoice->amount   , 136.85, "$testname: amount ok");
775
776   my $bt            = create_bank_transaction(record        => $invoice,
777                                               amount        => $invoice->amount-100,
778                                               bank_chart_id => $bank->id,
779                                               transdate     => DateTime->today->add(days => 10),
780                                              );
781   $::form->{invoice_ids} = {
782     $bt->id => [ $invoice->id ]
783   };
784
785   save_btcontroller_to_string();
786
787   $invoice->load;
788   $bt->load;
789
790   is($invoice->amount   , '136.85000', "$testname: amount ok");
791   is($invoice->netamount, '115.00000', "$testname: netamount ok");
792   is($bt->amount,         '-36.85000', "$testname: bt amount ok");
793   is($invoice->paid     ,  '36.85000', "$testname: paid ok");
794   is($bt->invoice_amount, '-36.85000', "$testname: bt invoice amount for ap was assigned");
795
796   my $bt2           = create_bank_transaction(record        => $invoice,
797                                               amount        => 100,
798                                               bank_chart_id => $bank->id,
799                                               transdate     => DateTime->today->add(days => 10),
800                                              );
801   $::form->{invoice_ids} = {
802     $bt2->id => [ $invoice->id ]
803   };
804
805   save_btcontroller_to_string();
806   $invoice->load;
807   $bt2->load;
808
809   is($invoice->amount   , '136.85000', "$testname: amount ok");
810   is($invoice->netamount, '115.00000', "$testname: netamount ok");
811   is($bt2->amount,        '-100.00000',"$testname: bt amount ok");
812   is($invoice->paid     , '136.85000', "$testname: paid ok");
813   is($bt2->invoice_amount,'-100.00000', "$testname: bt invoice amount for ap was assigned");
814
815   return $invoice;
816 };
817
818 sub test_neg_sales_invoice {
819
820   my $testname = 'test_neg_sales_invoice';
821
822   my $part1 = new_part(   partnumber => 'Funkenhaube öhm')->save;
823   my $part2 = new_service(partnumber => 'Service-Pauschale Pasch!')->save;
824
825   my $neg_sales_inv = create_sales_invoice(
826     invnumber    => '20172201',
827     customer     => $customer,
828     taxincluded  => 0,
829     invoiceitems => [ create_invoice_item(part => $part1, qty =>  3, sellprice => 70),
830                       create_invoice_item(part => $part2, qty => 10, sellprice => -50),
831                     ]
832   );
833   my $bt            = create_bank_transaction(record        => $neg_sales_inv,
834                                                                 amount        => $neg_sales_inv->amount,
835                                                                 bank_chart_id => $bank->id,
836                                                                 transdate     => DateTime->today,
837                                                                );
838   $::form->{invoice_ids} = {
839     $bt->id => [ $neg_sales_inv->id ]
840   };
841
842   save_btcontroller_to_string();
843
844   $neg_sales_inv->load;
845   $bt->load;
846   is($neg_sales_inv->amount   , '-345.10000', "$testname: amount ok");
847   is($neg_sales_inv->netamount, '-290.00000', "$testname: netamount ok");
848   is($neg_sales_inv->paid     , '-345.10000', "$testname: paid ok");
849   is($bt->amount              , '-345.10000', "$testname: bt amount ok");
850   is($bt->invoice_amount      , '-345.10000', "$testname: bt invoice_amount ok");
851 }
852
853 sub test_bt_rule1 {
854
855   my $testname = 'test_bt_rule1';
856
857   $ar_transaction = test_ar_transaction(invnumber => 'bt_rule1');
858
859   my $bt = create_bank_transaction(record => $ar_transaction) or die "Couldn't create bank_transaction";
860
861   $ar_transaction->load;
862   $bt->load;
863   is($ar_transaction->paid   , '0.00000' , "$testname: not paid");
864   is($bt->invoice_amount     , '0.00000' , "$testname: bt invoice amount was not assigned");
865
866   my $bt_controller = SL::Controller::BankTransaction->new;
867   $::form->{dont_render_for_test} = 1;
868   $::form->{filter}{bank_account} = $bank_account->id;
869   my ( $bt_transactions, $proposals ) = $bt_controller->action_list;
870
871   is(scalar(@$bt_transactions)         , 1  , "$testname: one bank_transaction");
872   is($bt_transactions->[0]->{agreement}, 20 , "$testname: agreement == 20");
873   my $match = join ( ' ',@{$bt_transactions->[0]->{rule_matches}});
874   #print "rule_matches='".$match."'\n";
875   is($match,
876      "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) ",
877      "$testname: rule_matches ok");
878   $bt->invoice_amount($bt->amount);
879   $bt->save;
880   is($bt->invoice_amount     , '119.00000' , "$testname: bt invoice amount now set");
881 };
882
883 sub test_sepa_export {
884
885   my $testname = 'test_sepa_export';
886
887   $ar_transaction = test_ar_transaction(invnumber => 'sepa1');
888
889   my $bt  = create_bank_transaction(record => $ar_transaction) or die "Couldn't create bank_transaction";
890   my $se  = create_sepa_export();
891   my $sei = create_sepa_export_item(
892     chart_id       => $bank->id,
893     ar_id          => $ar_transaction->id,
894     sepa_export_id => $se->id,
895     vc_iban        => $customer->iban,
896     vc_bic         => $customer->bic,
897     vc_mandator_id => $customer->mandator_id,
898     vc_depositor   => $customer->depositor,
899     amount         => $ar_transaction->amount,
900   );
901   require SL::SEPA::XML;
902   my $sepa_xml   = SL::SEPA::XML->new('company'     => $customer->name,
903                                       'creditor_id' => "id",
904                                       'src_charset' => 'UTF-8',
905                                       'message_id'  => "test",
906                                       'grouped'     => 1,
907                                       'collection'  => 1,
908                                      );
909   is($sepa_xml->{company}    , 'Test Customer OLE S.L. Ardbaerg AB');
910
911   $ar_transaction->load;
912   $bt->load;
913   $sei->load;
914   is($ar_transaction->paid   , '0.00000' , "$testname: sepa1 not paid");
915   is($bt->invoice_amount     , '0.00000' , "$testname: bt invoice amount was not assigned");
916   is($bt->amount             , '119.00000' , "$testname: bt amount ok");
917   is($sei->amount            , '119.00000' , "$testname: sepa export amount ok");
918
919   my $bt_controller = SL::Controller::BankTransaction->new;
920   $::form->{dont_render_for_test} = 1;
921   $::form->{filter}{bank_account} = $bank_account->id;
922   my ( $bt_transactions, $proposals ) = $bt_controller->action_list;
923
924   is(scalar(@$bt_transactions)         , 1  , "$testname: one bank_transaction");
925   is($bt_transactions->[0]->{agreement}, 25 , "$testname: agreement == 25");
926   my $match = join ( ' ',@{$bt_transactions->[0]->{rule_matches}});
927   is($match,
928      "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) ",
929      "$testname: rule_matches ok");
930 };
931
932 sub test_two_banktransactions {
933
934   my $testname = 'two_banktransactions';
935
936   my $ar_transaction_1 = test_ar_transaction(invnumber => 'salesinv10000' , amount => 2912.00 );
937   my $bt1 = create_bank_transaction(record        => $ar_transaction_1,
938                                     amount        => $ar_transaction_1->amount,
939                                     purpose       => "Rechnung10000 beinahe",
940                                     bank_chart_id => $bank->id,
941                                   ) or die "Couldn't create bank_transaction";
942
943   my $bt2 = create_bank_transaction(record        => $ar_transaction_1,
944                                     amount        => $ar_transaction_1->amount + 0.01,
945                                     purpose       => "sicher salesinv20000 vielleicht",
946                                     bank_chart_id => $bank->id,
947                                   ) or die "Couldn't create bank_transaction";
948
949   my ($agreement1, $rule_matches1) = $bt1->get_agreement_with_invoice($ar_transaction_1);
950   is($agreement1, 19, "bt1 19 points for ar_transaction_1 in $testname ok");
951   #print "rule_matches1=".$rule_matches1."\n";
952   is($rule_matches1,
953      "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) ",
954      "$testname: rule_matches ok");
955   my ($agreement2, $rule_matches2) = $bt2->get_agreement_with_invoice($ar_transaction_1);
956   is($agreement2, 11, "bt2 11 points for ar_transaction_1 in $testname ok");
957   is($rule_matches2,
958      "remote_account_number(3) depositor_matches(2) remote_name(2) payment_within_30_days(1) datebonus0(3) ",
959      "$testname: rule_matches ok");
960
961   my $ar_transaction_2 = test_ar_transaction(invnumber => 'salesinv20000' , amount => 2912.01 );
962   my $ar_transaction_3 = test_ar_transaction(invnumber => 'zweitemit10000', amount => 2912.00 );
963      ($agreement1, $rule_matches1) = $bt1->get_agreement_with_invoice($ar_transaction_2);
964
965   is($agreement1, 11, "bt1 11 points for ar_transaction_2 in $testname ok");
966
967      ($agreement2, $rule_matches2) = $bt2->get_agreement_with_invoice($ar_transaction_2);
968   is($agreement2, 20, "bt2 20 points for ar_transaction_2 in $testname ok");
969
970      ($agreement2, $rule_matches2) = $bt2->get_agreement_with_invoice($ar_transaction_1);
971   is($agreement2, 11, "bt2 11 points for ar_transaction_1 in $testname ok");
972
973   my $bt3 = create_bank_transaction(record        => $ar_transaction_3,
974                                     amount        => $ar_transaction_3->amount,
975                                     purpose       => "sicher Rechnung10000 vielleicht",
976                                     bank_chart_id => $bank->id,
977                                   ) or die "Couldn't create bank_transaction";
978
979   my ($agreement3, $rule_matches3) = $bt3->get_agreement_with_invoice($ar_transaction_3);
980   is($agreement3, 19, "bt3 19 points for ar_transaction_3 in $testname ok");
981
982   $bt2->delete;
983   $ar_transaction_2->delete;
984
985   #nun sollten zwei gleichwertige Rechnungen $ar_transaction_1 und $ar_transaction_3 für $bt1 gefunden werden
986   #aber es darf keine Proposals geben mit mehreren Rechnungen
987   my $bt_controller = SL::Controller::BankTransaction->new;
988   $::form->{dont_render_for_test} = 1;
989   $::form->{filter}{bank_account} = $bank_account->id;
990   my ( $bt_transactions, $proposals ) = $bt_controller->action_list;
991
992   is(scalar(@$bt_transactions)   , 2  , "$testname: two bank_transaction");
993   is(scalar(@$proposals)         , 0  , "$testname: no proposals");
994
995   $ar_transaction_3->delete;
996
997   # Jetzt gibt es zwei Kontobewegungen mit gleichen Punkten für eine Rechnung.
998   # hier darf es auch keine Proposals geben
999
1000   ( $bt_transactions, $proposals ) = $bt_controller->action_list;
1001
1002   is(scalar(@$bt_transactions)   , 2  , "$testname: two bank_transaction");
1003   is(scalar(@$proposals)         , 0  , "$testname: no proposals");
1004
1005   # Jetzt gibt es zwei Kontobewegungen für eine Rechnung.
1006   # eine Bewegung bekommt mehr Punkte
1007   # hier darf es auch keine Proposals geben
1008   $bt3->update_attributes( purpose => "fuer Rechnung salesinv10000");
1009
1010   ( $bt_transactions, $proposals ) = $bt_controller->action_list;
1011
1012   is(scalar(@$bt_transactions)   , 2  , "$testname: two bank_transaction");
1013   is(scalar(@$proposals)         , 1  , "$testname: one proposal");
1014
1015 };
1016
1017
1018 1;