Payment Helper kann nun auch Kursverluste/Gewinne
[kivitendo-erp.git] / t / db_helper / payment.t
1 use Test::More;
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::Buchungsgruppe;
14 use SL::DB::Currency;
15 use SL::DB::Exchangerate;
16 use SL::DB::Customer;
17 use SL::DB::Vendor;
18 use SL::DB::Employee;
19 use SL::DB::Invoice;
20 use SL::DB::Part;
21 use SL::DB::Unit;
22 use SL::DB::TaxZone;
23 use SL::DB::BankAccount;
24 use SL::DB::PaymentTerm;
25 use SL::DBUtils qw(selectfirst_array_query);
26 use Data::Dumper;
27
28 my ($customer, $vendor, $currency_id, @parts, $buchungsgruppe, $buchungsgruppe7, $unit, $employee, $tax, $tax7, $tax_9, $taxzone, $payment_terms, $bank_account);
29 my ($transdate1, $transdate2, $transdate3, $transdate4, $currency, $exchangerate, $exchangerate2, $exchangerate3, $exchangerate4);
30 my ($ar_chart,$bank,$ar_amount_chart, $ap_chart, $ap_amount_chart, $fxloss_chart, $fxgain_chart);
31
32 my $ALWAYS_RESET = 1;
33
34 my $reset_state_counter = 0;
35
36 my $purchase_invoice_counter = 0; # used for generating purchase invnumber
37
38 sub clear_up {
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::BankAccount->delete_all(all => 1);
46   SL::DB::Manager::PaymentTerm->delete_all(all => 1);
47   SL::DB::Manager::Exchangerate->delete_all(all => 1);
48   SL::DB::Manager::Currency->delete_all(where => [ name => 'CUR' ]);
49 };
50
51 sub reset_state {
52   my %params = @_;
53
54   return if $reset_state_counter;
55
56   $params{$_} ||= {} for qw(buchungsgruppe unit customer part tax vendor);
57
58   clear_up();
59
60   $transdate1 = DateTime->today;
61   $transdate2 = DateTime->today->add(days => 1);
62   $transdate3 = DateTime->today->add(days => 2);
63   $transdate4 = DateTime->today->add(days => 3);
64
65   $buchungsgruppe  = SL::DB::Manager::Buchungsgruppe->find_by(description => 'Standard 19%', %{ $params{buchungsgruppe} }) || croak "No accounting group";
66   $buchungsgruppe7 = SL::DB::Manager::Buchungsgruppe->find_by(description => 'Standard 7%')                                || croak "No accounting group for 7\%";
67   $unit            = SL::DB::Manager::Unit->find_by(name => 'kg', %{ $params{unit} })                                      || croak "No unit";
68   $employee        = SL::DB::Manager::Employee->current                                                                    || croak "No employee";
69   $tax             = SL::DB::Manager::Tax->find_by(taxkey => 3, rate => 0.19, %{ $params{tax} })                           || croak "No tax";
70   $tax7            = SL::DB::Manager::Tax->find_by(taxkey => 2, rate => 0.07)                                              || croak "No tax for 7\%";
71   $taxzone         = SL::DB::Manager::TaxZone->find_by( description => 'Inland')                                           || croak "No taxzone";
72   $tax_9           = SL::DB::Manager::Tax->find_by(taxkey => 9, rate => 0.19, %{ $params{tax} })                           || croak "No tax";
73   # $tax7            = SL::DB::Manager::Tax->find_by(taxkey => 2, rate => 0.07)                                              || croak "No tax for 7\%";
74
75   $currency_id     = $::instance_conf->get_currency_id;
76
77   $currency = SL::DB::Currency->new(name => 'CUR')->save;
78
79   $fxgain_chart = SL::DB::Manager::Chart->find_by(accno => '2660') or die "Can't find fxgain_chart in test";
80   $fxloss_chart = SL::DB::Manager::Chart->find_by(accno => '2150') or die "Can't find fxloss_chart in test";
81
82   $currency->db->dbh->do('UPDATE defaults SET fxgain_accno_id = ' . $fxgain_chart->id);
83   $currency->db->dbh->do('UPDATE defaults SET fxloss_accno_id = ' . $fxloss_chart->id);
84   $::instance_conf->reload->data;
85   is($fxgain_chart->id,  $::instance_conf->get_fxgain_accno_id, "fxgain_chart was updated in defaults");
86   is($fxloss_chart->id,  $::instance_conf->get_fxloss_accno_id, "fxloss_chart was updated in defaults");
87
88   $exchangerate  = SL::DB::Exchangerate->new(transdate   => $transdate1,
89                                              buy         => '1.33333',
90                                              sell        => '1.33333',
91                                              currency_id => $currency->id,
92                                             )->save;
93   $exchangerate2 = SL::DB::Exchangerate->new(transdate   => $transdate2,
94                                              buy         => '0.8',
95                                              sell        => '0.8',
96                                              currency_id => $currency->id,
97                                             )->save;
98   $exchangerate3 = SL::DB::Exchangerate->new(transdate   => $transdate3,
99                                              buy         => '1.55557',
100                                              sell        => '1.55557',
101                                              currency_id => $currency->id,
102                                             )->save;
103   $exchangerate4 = SL::DB::Exchangerate->new(transdate   => $transdate4,
104                                              buy         => '0.77777',
105                                              sell        => '0.77777',
106                                              currency_id => $currency->id,
107                                             )->save;
108
109   $customer     = SL::DB::Customer->new(
110     name        => 'Test Customer',
111     currency_id => $currency_id,
112     taxzone_id  => $taxzone->id,
113     %{ $params{customer} }
114   )->save;
115
116   $bank_account     =  SL::DB::BankAccount->new(
117     account_number  => '123',
118     bank_code       => '123',
119     iban            => '123',
120     bic             => '123',
121     bank            => '123',
122     chart_id        => SL::DB::Manager::Chart->find_by( description => 'Bank' )->id,
123     name            => SL::DB::Manager::Chart->find_by( description => 'Bank' )->description,
124   )->save;
125
126   $payment_terms     =  SL::DB::PaymentTerm->new(
127     description      => 'payment',
128     description_long => 'payment',
129     terms_netto      => '30',
130     terms_skonto     => '5',
131     percent_skonto   => '0.05',
132     auto_calculation => 1,
133   )->save;
134
135   $vendor       = SL::DB::Vendor->new(
136     name        => 'Test Vendor',
137     currency_id => $currency_id,
138     taxzone_id  => $taxzone->id,
139     payment_id  => $payment_terms->id,
140     %{ $params{vendor} }
141   )->save;
142
143
144   @parts = ();
145   push @parts, SL::DB::Part->new(
146     partnumber         => 'T4254',
147     description        => 'Fourty-two fifty-four',
148     lastcost           => 1.93,
149     sellprice          => 2.34,
150     buchungsgruppen_id => $buchungsgruppe->id,
151     unit               => $unit->name,
152     %{ $params{part1} }
153   )->save;
154
155   push @parts, SL::DB::Part->new(
156     partnumber         => 'T0815',
157     description        => 'Zero EIGHT fifteeN @ 7%',
158     lastcost           => 5.473,
159     sellprice          => 9.714,
160     buchungsgruppen_id => $buchungsgruppe7->id,
161     unit               => $unit->name,
162     %{ $params{part2} }
163   )->save;
164   push @parts, SL::DB::Part->new(
165     partnumber         => '19%',
166     description        => 'Testware 19%',
167     lastcost           => 0,
168     sellprice          => 50,
169     buchungsgruppen_id => $buchungsgruppe->id,
170     unit               => $unit->name,
171     %{ $params{part3} }
172   )->save;
173   push @parts, SL::DB::Part->new(
174     partnumber         => '7%',
175     description        => 'Testware 7%',
176     lastcost           => 0,
177     sellprice          => 50,
178     buchungsgruppen_id => $buchungsgruppe7->id,
179     unit               => $unit->name,
180     %{ $params{part4} }
181   )->save;
182
183   $ar_chart        = SL::DB::Manager::Chart->find_by( accno => '1400' ); # Forderungen
184   $ap_chart        = SL::DB::Manager::Chart->find_by( accno => '1600' ); # Verbindlichkeiten
185   $bank            = SL::DB::Manager::Chart->find_by( accno => '1200' ); # Bank
186   $ar_amount_chart = SL::DB::Manager::Chart->find_by( accno => '8400' ); # Erlöse
187   $ap_amount_chart = SL::DB::Manager::Chart->find_by( accno => '3400' ); # Wareneingang 19%
188
189   $reset_state_counter++;
190 }
191
192 sub new_invoice {
193   my %params  = @_;
194
195   return SL::DB::Invoice->new(
196     customer_id => $customer->id,
197     currency_id => $currency_id,
198     employee_id => $employee->id,
199     salesman_id => $employee->id,
200     gldate      => $transdate1,
201     taxzone_id  => $taxzone->id,
202     transdate   => $transdate1,
203     invoice     => 1,
204     type        => 'invoice',
205     %params,
206   );
207
208 }
209
210 sub new_purchase_invoice {
211   # my %params  = @_;
212   # manually create a Kreditorenbuchung from scratch, ap + acc_trans bookings, as no helper exists yet, like $invoice->post.
213   # arap-Booking must come last in the acc_trans order
214   $purchase_invoice_counter++;
215
216   my $purchase_invoice = SL::DB::PurchaseInvoice->new(
217     vendor_id   => $vendor->id,
218     invnumber   => 'newap ' . $purchase_invoice_counter ,
219     currency_id => $currency_id,
220     employee_id => $employee->id,
221     gldate      => $transdate1,
222     taxzone_id  => $taxzone->id,
223     transdate   => $transdate1,
224     invoice     => 0,
225     type        => 'invoice',
226     taxincluded => 0,
227     amount      => '226',
228     netamount   => '200',
229     paid        => '0',
230     # %params,
231   )->save;
232
233   my $expense_chart  = SL::DB::Manager::Chart->find_by(accno => '3400');
234   my $expense_chart_booking= SL::DB::AccTransaction->new(
235                                         trans_id   => $purchase_invoice->id,
236                                         chart_id   => $expense_chart->id,
237                                         chart_link => $expense_chart->link,
238                                         amount     => '-100',
239                                         transdate  => $transdate1,
240                                         source     => '',
241                                         taxkey     => 9,
242                                         tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 9)->id);
243   $expense_chart_booking->save;
244
245   my $tax_chart  = SL::DB::Manager::Chart->find_by(accno => '1576');
246   my $tax_chart_booking= SL::DB::AccTransaction->new(
247                                         trans_id   => $purchase_invoice->id,
248                                         chart_id   => $tax_chart->id,
249                                         chart_link => $tax_chart->link,
250                                         amount     => '-19',
251                                         transdate  => $transdate1,
252                                         source     => '',
253                                         taxkey     => 0,
254                                         tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 9)->id);
255   $tax_chart_booking->save;
256   $expense_chart  = SL::DB::Manager::Chart->find_by(accno => '3300');
257   $expense_chart_booking= SL::DB::AccTransaction->new(
258                                         trans_id   => $purchase_invoice->id,
259                                         chart_id   => $expense_chart->id,
260                                         chart_link => $expense_chart->link,
261                                         amount     => '-100',
262                                         transdate  => $transdate1,
263                                         source     => '',
264                                         taxkey     => 8,
265                                         tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 8)->id);
266   $expense_chart_booking->save;
267
268
269   $tax_chart  = SL::DB::Manager::Chart->find_by(accno => '1571');
270   $tax_chart_booking= SL::DB::AccTransaction->new(
271                                          trans_id   => $purchase_invoice->id,
272                                          chart_id   => $tax_chart->id,
273                                          chart_link => $tax_chart->link,
274                                          amount     => '-7',
275                                          transdate  => $transdate1,
276                                          source     => '',
277                                          taxkey     => 0,
278                                          tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 8)->id);
279   $tax_chart_booking->save;
280   my $arap_chart  = SL::DB::Manager::Chart->find_by(accno => '1600');
281   my $arap_booking= SL::DB::AccTransaction->new(trans_id   => $purchase_invoice->id,
282                                                 chart_id   => $arap_chart->id,
283                                                 chart_link => $arap_chart->link,
284                                                 amount     => '226',
285                                                 transdate  => $transdate1,
286                                                 source     => '',
287                                                 taxkey     => 0,
288                                                 tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 0)->id);
289   $arap_booking->save;
290
291   return $purchase_invoice;
292 }
293
294 sub new_item {
295   my (%params) = @_;
296
297   my $part = delete($params{part}) || $parts[0];
298
299   return SL::DB::InvoiceItem->new(
300     parts_id    => $part->id,
301     lastcost    => $part->lastcost,
302     sellprice   => $part->sellprice,
303     description => $part->description,
304     unit        => $part->unit,
305     %params,
306   );
307 }
308
309 sub number_of_payments {
310   my $invoice = shift;
311
312   my $number_of_payments;
313   my $paid_amount;
314   foreach my $transaction ( @{ $invoice->transactions } ) {
315     if ( $transaction->chart_link =~ /(AR_paid|AP_paid)/ ) {
316       $paid_amount += $transaction->amount ;
317       $number_of_payments++;
318     };
319   };
320   return ($number_of_payments, $paid_amount);
321 };
322
323 sub total_amount {
324   my $invoice = shift;
325
326   my $total = sum map { $_->amount } @{ $invoice->transactions };
327
328   return $::form->round_amount($total, 5);
329
330 };
331
332
333 # test 1
334 sub test_default_invoice_one_item_19_without_skonto() {
335   reset_state() if $ALWAYS_RESET;
336
337   my $item    = new_item(qty => 2.5);
338   my $invoice = new_invoice(
339     taxincluded  => 0,
340     invoiceitems => [ $item ],
341     payment_id   => $payment_terms->id,
342   );
343   $invoice->post;
344
345   my $purchase_invoice = new_purchase_invoice();
346
347
348   # default values
349   my %params = ( chart_id => $bank_account->chart_id,
350                  transdate => DateTime->today_local->to_kivitendo
351                );
352
353   $params{amount} = '6.96';
354   $params{payment_type} = 'without_skonto';
355
356   $invoice->pay_invoice( %params );
357
358   my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
359   my $total = total_amount($invoice);
360
361   my $title = 'default invoice, one item, 19% tax, without_skonto';
362
363   is($invoice->netamount,   5.85,      "${title}: netamount");
364   is($invoice->amount,      6.96,      "${title}: amount");
365   is($paid_amount,         -6.96,      "${title}: paid amount");
366   is($number_of_payments,      1,      "${title}: 1 AR_paid booking");
367   is($invoice->paid,        6.96,      "${title}: paid");
368   is($total,                   0,      "${title}: even balance");
369
370 }
371
372 sub test_default_invoice_one_item_19_without_skonto_overpaid() {
373   reset_state() if $ALWAYS_RESET;
374
375   my $item    = new_item(qty => 2.5);
376   my $invoice = new_invoice(
377     taxincluded  => 0,
378     invoiceitems => [ $item ],
379     payment_id   => $payment_terms->id,
380   );
381   $invoice->post;
382
383   my $purchase_invoice = new_purchase_invoice();
384
385
386   # default values
387   my %params = ( chart_id => $bank_account->chart_id,
388                  transdate => DateTime->today_local->to_kivitendo
389                );
390
391   $params{amount} = '16.96';
392   $params{payment_type} = 'without_skonto';
393   $invoice->pay_invoice( %params );
394
395   $params{amount} = '-10.00';
396   $invoice->pay_invoice( %params );
397
398   my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
399   my $total = total_amount($invoice);
400
401   my $title = 'default invoice, one item, 19% tax, without_skonto';
402
403   is($invoice->netamount,   5.85,      "${title}: netamount");
404   is($invoice->amount,      6.96,      "${title}: amount");
405   is($paid_amount,         -6.96,      "${title}: paid amount");
406   is($number_of_payments,      2,      "${title}: 1 AR_paid booking");
407   is($invoice->paid,        6.96,      "${title}: paid");
408   is($total,                   0,      "${title}: even balance");
409
410 }
411
412
413 # test 2
414 sub test_default_invoice_two_items_19_7_tax_with_skonto() {
415   reset_state() if $ALWAYS_RESET;
416
417   my $item1   = new_item(qty => 2.5);
418   my $item2   = new_item(qty => 1.2, part => $parts[1]);
419   my $invoice = new_invoice(
420     taxincluded  => 0,
421     invoiceitems => [ $item1, $item2 ],
422     payment_id  => $payment_terms->id,
423   );
424   $invoice->post;
425
426   # default values
427   my %params = ( chart_id => $bank_account->chart_id,
428                  transdate => DateTime->today_local->to_kivitendo
429                );
430
431   $params{payment_type} = 'with_skonto_pt';
432   $params{amount}       = $invoice->amount_less_skonto;
433
434   $invoice->pay_invoice( %params );
435
436   my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
437   my $total = total_amount($invoice);
438
439   my $title = 'default invoice, two items, 19/7% tax with_skonto_pt';
440
441   is($invoice->netamount,  5.85 + 11.66,   "${title}: netamount");
442   is($invoice->amount,     6.96 + 12.48,   "${title}: amount");
443   is($paid_amount,               -19.44,   "${title}: paid amount");
444   is($invoice->paid,              19.44,   "${title}: paid");
445   is($number_of_payments,             3,   "${title}: 3 AR_paid bookings");
446   is($total,                          0,   "${title}: even balance");
447 }
448
449 sub test_default_invoice_two_items_19_7_tax_with_skonto_tax_included() {
450   reset_state() if $ALWAYS_RESET;
451
452   my $item1   = new_item(qty => 2.5);
453   my $item2   = new_item(qty => 1.2, part => $parts[1]);
454   my $invoice = new_invoice(
455     taxincluded  => 1,
456     invoiceitems => [ $item1, $item2 ],
457     payment_id  => $payment_terms->id,
458   );
459   $invoice->post;
460
461   # default values
462   my %params = ( chart_id => $bank_account->chart_id,
463                  transdate => DateTime->today_local->to_kivitendo
464                );
465
466   $params{payment_type} = 'with_skonto_pt';
467   $params{amount}       = $invoice->amount_less_skonto;
468
469   $invoice->pay_invoice( %params );
470
471   my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
472   my $total = total_amount($invoice);
473
474   my $title = 'default invoice, two items, 19/7% tax with_skonto_pt';
475
476   is($invoice->netamount,         15.82,   "${title}: netamount");
477   is($invoice->amount,            17.51,   "${title}: amount");
478   is($paid_amount,               -17.51,   "${title}: paid amount");
479   is($invoice->paid,              17.51,   "${title}: paid");
480   is($number_of_payments,             3,   "${title}: 3 AR_paid bookings");
481   { local $TODO = "currently this test fails because the code writing the invoice is buggy, the calculation of skonto is correct";
482   is($total,                          0,   "${title}: even balance");
483   }
484 }
485
486 # test 3 : two items, without skonto
487 sub test_default_invoice_two_items_19_7_without_skonto() {
488   reset_state() if $ALWAYS_RESET;
489
490   my $item1   = new_item(qty => 2.5);
491   my $item2   = new_item(qty => 1.2, part => $parts[1]);
492   my $invoice = new_invoice(
493     taxincluded  => 0,
494     invoiceitems => [ $item1, $item2 ],
495     payment_id  => $payment_terms->id,
496   );
497   $invoice->post;
498
499   # default values
500   my %params = ( chart_id => $bank_account->chart_id,
501                  transdate => DateTime->today_local->to_kivitendo
502                );
503
504   $params{amount} = '19.44'; # pass full amount
505   $params{payment_type} = 'without_skonto';
506
507   $invoice->pay_invoice( %params );
508
509   my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
510   my $total = total_amount($invoice);
511
512   my $title = 'default invoice, two items, 19/7% tax without skonto';
513
514   is($invoice->netamount,     5.85 + 11.66,     "${title}: netamount");
515   is($invoice->amount,        6.96 + 12.48,     "${title}: amount");
516   is($paid_amount,                  -19.44,     "${title}: paid amount");
517   is($invoice->paid,                 19.44,     "${title}: paid");
518   is($number_of_payments,                1,     "${title}: 1 AR_paid bookings");
519   is($total,                             0,     "${title}: even balance");
520 }
521
522 # test 4
523 sub test_default_invoice_two_items_19_7_without_skonto_incomplete_payment() {
524   reset_state() if $ALWAYS_RESET;
525
526   my $item1   = new_item(qty => 2.5);
527   my $item2   = new_item(qty => 1.2, part => $parts[1]);
528   my $invoice = new_invoice(
529     taxincluded  => 0,
530     invoiceitems => [ $item1, $item2 ],
531     payment_id  => $payment_terms->id,
532   );
533   $invoice->post;
534
535   $invoice->pay_invoice( amount       => '9.44',
536                          payment_type => 'without_skonto',
537                          chart_id     => $bank_account->chart_id,
538                          transdate    => DateTime->today_local->to_kivitendo,
539                        );
540
541   my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
542   my $total = total_amount($invoice);
543
544   my $title = 'default invoice, two items, 19/7% tax without skonto incomplete payment';
545
546   is($invoice->netamount,        5.85 + 11.66,     "${title}: netamount");
547   is($invoice->amount,           6.96 + 12.48,     "${title}: amount");
548   is($paid_amount,              -9.44,             "${title}: paid amount");
549   is($invoice->paid,             9.44,            "${title}: paid");
550   is($number_of_payments,   1,                "${title}: 1 AR_paid bookings");
551   is($total,                    0,                "${title}: even balance");
552 }
553
554 # test 5
555 sub test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments() {
556   reset_state() if $ALWAYS_RESET;
557
558   my $item1   = new_item(qty => 2.5);
559   my $item2   = new_item(qty => 1.2, part => $parts[1]);
560   my $invoice = new_invoice(
561     taxincluded  => 0,
562     invoiceitems => [ $item1, $item2 ],
563     payment_id  => $payment_terms->id,
564   );
565   $invoice->post;
566
567   $invoice->pay_invoice( amount       => '9.44',
568                          payment_type => 'without_skonto',
569                          chart_id     => $bank_account->chart_id,
570                          transdate    => DateTime->today_local->to_kivitendo
571                        );
572   $invoice->pay_invoice( amount       => '10.00',
573                          chart_id     => $bank_account->chart_id,
574                          transdate    => DateTime->today_local->to_kivitendo
575                        );
576
577   my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
578   my $total = total_amount($invoice);
579
580   my $title = 'default invoice, two items, 19/7% tax not included';
581
582   is($invoice->netamount,        5.85 + 11.66,     "${title}: netamount");
583   is($invoice->amount,           6.96 + 12.48,     "${title}: amount");
584   is($paid_amount,                     -19.44,     "${title}: paid amount");
585   is($invoice->paid,                    19.44,     "${title}: paid");
586   is($number_of_payments,                   2,     "${title}: 2 AR_paid bookings");
587   is($total,                                0,     "${title}: even balance");
588
589 }
590
591 # test 6
592 sub test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto() {
593   reset_state() if $ALWAYS_RESET;
594
595   my $item1   = new_item(qty => 2.5);
596   my $item2   = new_item(qty => 1.2, part => $parts[1]);
597   my $invoice = new_invoice(
598     taxincluded  => 0,
599     invoiceitems => [ $item1, $item2 ],
600     payment_id  => $payment_terms->id,
601   );
602   $invoice->post;
603
604   $invoice->pay_invoice( amount       => '9.44',
605                          payment_type => 'without_skonto',
606                          chart_id     => $bank_account->chart_id,
607                          transdate    => DateTime->today_local->to_kivitendo
608                        );
609   $invoice->pay_invoice( amount       => '8.73',
610                          payment_type => 'without_skonto',
611                          chart_id     => $bank_account->chart_id,
612                          transdate    => DateTime->today_local->to_kivitendo
613                        );
614   $invoice->pay_invoice( amount       => $invoice->open_amount,
615                          payment_type => 'difference_as_skonto',
616                          chart_id     => $bank_account->chart_id,
617                          transdate    => DateTime->today_local->to_kivitendo
618                        );
619
620   my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
621   my $total = total_amount($invoice);
622
623   my $title = 'default invoice, two items, 19/7% tax not included';
624
625   is($invoice->netamount,        5.85 + 11.66,     "${title}: netamount");
626   is($invoice->amount,           6.96 + 12.48,     "${title}: amount");
627   is($paid_amount,                     -19.44,     "${title}: paid amount");
628   is($invoice->paid,                    19.44,     "${title}: paid");
629   is($number_of_payments,                   4,     "${title}: 4 AR_paid bookings");
630   is($total,                                0,     "${title}: even balance");
631
632 }
633
634 sub  test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto_1cent() {
635   reset_state() if $ALWAYS_RESET;
636
637   # if there is only one cent left there can only be one skonto booking, the
638   # error handling should choose the highest amount, which is the 7% account
639   # (11.66) rather than the 19% account (5.85).  The actual tax amount is
640   # higher for the 19% case, though (1.11 compared to 0.82)
641
642   my $item1   = new_item(qty => 2.5);
643   my $item2   = new_item(qty => 1.2, part => $parts[1]);
644   my $invoice = new_invoice(
645     taxincluded  => 0,
646     invoiceitems => [ $item1, $item2 ],
647     payment_id  => $payment_terms->id,
648   );
649   $invoice->post;
650
651   $invoice->pay_invoice( amount       => '19.42',
652                          payment_type => 'without_skonto',
653                          chart_id     => $bank_account->chart_id,
654                          transdate    => DateTime->today_local->to_kivitendo
655                        );
656   $invoice->pay_invoice( amount       => $invoice->open_amount,
657                          payment_type => 'difference_as_skonto',
658                          chart_id     => $bank_account->chart_id,
659                          transdate    => DateTime->today_local->to_kivitendo
660                        );
661
662   my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
663   my $total = total_amount($invoice);
664
665   my $title = 'default invoice, two items, 19/7% tax not included';
666
667   is($invoice->netamount,        5.85 + 11.66,     "${title}: netamount");
668   is($invoice->amount,           6.96 + 12.48,     "${title}: amount");
669   is($paid_amount,                     -19.44,     "${title}: paid amount");
670   is($invoice->paid,                    19.44,     "${title}: paid");
671   is($number_of_payments,                   3,     "${title}: 2 AR_paid bookings");
672   is($total,                                0,     "${title}: even balance");
673
674 }
675
676 sub test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto_2cent() {
677   reset_state() if $ALWAYS_RESET;
678
679   # if there are two cents left there will be two skonto bookings, 1 cent each
680   my $item1   = new_item(qty => 2.5);
681   my $item2   = new_item(qty => 1.2, part => $parts[1]);
682   my $invoice = new_invoice(
683     taxincluded  => 0,
684     invoiceitems => [ $item1, $item2 ],
685     payment_id  => $payment_terms->id,
686   );
687   $invoice->post;
688
689   $invoice->pay_invoice( amount       => '19.42',
690                          payment_type => 'without_skonto',
691                          chart_id     => $bank_account->chart_id,
692                          transdate    => DateTime->today_local->to_kivitendo
693                        );
694   $invoice->pay_invoice( amount       => $invoice->open_amount,
695                          payment_type => 'difference_as_skonto',
696                          chart_id     => $bank_account->chart_id,
697                          transdate    => DateTime->today_local->to_kivitendo
698                        );
699
700   my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
701   my $total = total_amount($invoice);
702
703   my $title = 'default invoice, two items, 19/7% tax not included';
704
705   is($invoice->netamount,        5.85 + 11.66,     "${title}: netamount");
706   is($invoice->amount,           6.96 + 12.48,     "${title}: amount");
707   is($paid_amount,                     -19.44,     "${title}: paid amount");
708   is($invoice->paid,                    19.44,     "${title}: paid");
709   is($number_of_payments,                   3,     "${title}: 3 AR_paid bookings");
710   is($total,                                0,     "${title}: even balance");
711
712 }
713
714 sub test_default_invoice_one_item_19_multiple_payment_final_difference_as_skonto() {
715   reset_state() if $ALWAYS_RESET;
716
717   my $item    = new_item(qty => 2.5);
718   my $invoice = new_invoice(
719     taxincluded  => 0,
720     invoiceitems => [ $item ],
721     payment_id   => $payment_terms->id,
722   );
723   $invoice->post;
724
725   # default values
726   my %params = ( chart_id  => $bank_account->chart_id,
727                  transdate => DateTime->today_local->to_kivitendo
728                );
729
730   $params{amount}       = '2.32';
731   $params{payment_type} = 'without_skonto';
732   $invoice->pay_invoice( %params );
733
734   $params{amount}       = '3.81';
735   $params{payment_type} = 'without_skonto';
736   $invoice->pay_invoice( %params );
737
738   $params{amount}       = $invoice->open_amount; # set amount, otherwise previous 3.81 is used
739   $params{payment_type} = 'difference_as_skonto';
740   $invoice->pay_invoice( %params );
741
742   my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
743   my $total = total_amount($invoice);
744
745   my $title = 'default invoice, one item, 19% tax, without_skonto';
746
747   is($invoice->netamount,       5.85,     "${title}: netamount");
748   is($invoice->amount,          6.96,     "${title}: amount");
749   is($paid_amount,             -6.96,     "${title}: paid amount");
750   is($number_of_payments,          3,     "${title}: 3 AR_paid booking");
751   is($invoice->paid,            6.96,     "${title}: paid");
752   is($total,                       0,     "${title}: even balance");
753
754 }
755
756 sub test_default_invoice_one_item_19_multiple_payment_final_difference_as_skonto_1cent() {
757   reset_state() if $ALWAYS_RESET;
758
759   my $item    = new_item(qty => 2.5);
760   my $invoice = new_invoice(
761     taxincluded  => 0,
762     invoiceitems => [ $item ],
763     payment_id   => $payment_terms->id,
764   );
765   $invoice->post;
766
767   # default values
768   my %params = ( chart_id  => $bank_account->chart_id,
769                  transdate => DateTime->today_local->to_kivitendo
770                );
771
772   $params{amount}       = '6.95';
773   $params{payment_type} = 'without_skonto';
774   $invoice->pay_invoice( %params );
775
776   $params{amount}       = $invoice->open_amount; # set amount, otherwise previous value 6.95 is used
777   $params{payment_type} = 'difference_as_skonto';
778   $invoice->pay_invoice( %params );
779
780   my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
781   my $total = total_amount($invoice);
782
783   my $title = 'default invoice, one item, 19% tax, without_skonto';
784
785   is($invoice->netamount,       5.85,     "${title}: netamount");
786   is($invoice->amount,          6.96,     "${title}: amount");
787   is($paid_amount,             -6.96,     "${title}: paid amount");
788   is($number_of_payments,          2,     "${title}: 3 AR_paid booking");
789   is($invoice->paid,            6.96,     "${title}: paid");
790   is($total,                       0,     "${title}: even balance");
791
792 }
793
794 # test 3 : two items, without skonto
795 sub test_default_purchase_invoice_two_charts_19_7_without_skonto() {
796   reset_state() if $ALWAYS_RESET;
797
798   my $purchase_invoice = new_purchase_invoice();
799
800   my %params = ( chart_id => $bank_account->chart_id,
801                  transdate => DateTime->today_local->to_kivitendo
802                );
803
804   $params{amount} = '226'; # pass full amount
805   $params{payment_type} = 'without_skonto';
806
807   $purchase_invoice->pay_invoice( %params );
808
809   my ($number_of_payments, $paid_amount) = number_of_payments($purchase_invoice);
810   my $total = total_amount($purchase_invoice);
811
812   my $title = 'default invoice, two items, 19/7% tax without skonto';
813
814   is($paid_amount,         226,     "${title}: paid amount");
815   is($number_of_payments,    1,     "${title}: 1 AP_paid bookings");
816   is($total,                 0,     "${title}: even balance");
817
818 }
819
820 sub test_default_purchase_invoice_two_charts_19_7_with_skonto() {
821   reset_state() if $ALWAYS_RESET;
822
823   my $purchase_invoice = new_purchase_invoice();
824
825   my %params = ( chart_id => $bank_account->chart_id,
826                  transdate => DateTime->today_local->to_kivitendo
827                );
828
829   # $params{amount} = '226'; # pass full amount
830   $params{payment_type} = 'with_skonto_pt';
831
832   $purchase_invoice->pay_invoice( %params );
833
834   my ($number_of_payments, $paid_amount) = number_of_payments($purchase_invoice);
835   my $total = total_amount($purchase_invoice);
836
837   my $title = 'default invoice, two items, 19/7% tax without skonto';
838
839   is($paid_amount,         226,     "${title}: paid amount");
840   is($number_of_payments,    3,     "${title}: 1 AP_paid bookings");
841   is($total,                 0,     "${title}: even balance");
842
843 }
844
845 sub test_default_purchase_invoice_two_charts_19_7_tax_partial_unrounded_payment_without_skonto() {
846   # check whether unrounded amounts passed via $params{amount} are rounded for without_skonto case
847   reset_state() if $ALWAYS_RESET;
848   my $purchase_invoice = new_purchase_invoice();
849   $purchase_invoice->pay_invoice(
850                           amount       => ( $purchase_invoice->amount / 3 * 2),
851                           payment_type => 'without_skonto',
852                           chart_id     => $bank_account->chart_id,
853                           transdate    => DateTime->today_local->to_kivitendo
854                          );
855   my ($number_of_payments, $paid_amount) = number_of_payments($purchase_invoice);
856   my $total = total_amount($purchase_invoice);
857
858   my $title = 'default purchase_invoice, two charts, 19/7% tax multiple payments with final difference as skonto';
859
860   is($paid_amount,         150.67,   "${title}: paid amount");
861   is($number_of_payments,       1,   "${title}: 1 AP_paid bookings");
862   is($total,                    0,   "${title}: even balance");
863 };
864
865
866 sub test_default_purchase_invoice_two_charts_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto() {
867   reset_state() if $ALWAYS_RESET;
868
869   my $purchase_invoice = new_purchase_invoice();
870
871   # pay 2/3 and 1/5, leaves 3.83% to be used as Skonto
872   $purchase_invoice->pay_invoice(
873                           amount       => ( $purchase_invoice->amount / 3 * 2),
874                           payment_type => 'without_skonto',
875                           chart_id     => $bank_account->chart_id,
876                           transdate    => DateTime->today_local->to_kivitendo
877                          );
878   $purchase_invoice->pay_invoice(
879                           amount       => ( $purchase_invoice->amount / 5 ),
880                           payment_type => 'without_skonto',
881                           chart_id     => $bank_account->chart_id,
882                           transdate    => DateTime->today_local->to_kivitendo
883                          );
884   $purchase_invoice->pay_invoice(
885                           payment_type => 'difference_as_skonto',
886                           chart_id     => $bank_account->chart_id,
887                           transdate    => DateTime->today_local->to_kivitendo
888                          );
889
890   my ($number_of_payments, $paid_amount) = number_of_payments($purchase_invoice);
891   my $total = total_amount($purchase_invoice);
892
893   my $title = 'default purchase_invoice, two charts, 19/7% tax multiple payments with final difference as skonto';
894
895   is($paid_amount,         226, "${title}: paid amount");
896   is($number_of_payments,    4, "${title}: 1 AP_paid bookings");
897   is($total,                 0, "${title}: even balance");
898
899 }
900
901 # test
902 sub test_default_invoice_two_items_19_7_tax_with_skonto_50_50() {
903   reset_state() if $ALWAYS_RESET;
904
905   my $item1   = new_item(qty => 1, part => $parts[2]);
906   my $item2   = new_item(qty => 1, part => $parts[3]);
907   my $invoice = new_invoice(
908     taxincluded  => 0,
909     invoiceitems => [ $item1, $item2 ],
910     payment_id  => $payment_terms->id,
911   );
912   $invoice->post;
913
914   # default values
915   my %params = ( chart_id => $bank_account->chart_id,
916                  transdate => DateTime->today_local->to_kivitendo
917                );
918
919   $params{amount} = $invoice->amount_less_skonto;
920   $params{payment_type} = 'with_skonto_pt';
921
922   $invoice->pay_invoice( %params );
923
924   my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
925   my $total = total_amount($invoice);
926
927   my $title = 'default invoice, two items, 19/7% tax with_skonto_pt 50/50';
928
929   is($invoice->netamount,        100,     "${title}: netamount");
930   is($invoice->amount,           113,     "${title}: amount");
931   is($paid_amount,              -113,     "${title}: paid amount");
932   is($invoice->paid,             113,     "${title}: paid");
933   is($number_of_payments,          3,     "${title}: 3 AR_paid bookings");
934   is($total,                       0,     "${title}: even balance");
935 }
936
937 # test
938 sub test_default_invoice_four_items_19_7_tax_with_skonto_4x_25() {
939   reset_state() if $ALWAYS_RESET;
940
941   my $item1   = new_item(qty => 0.5, part => $parts[2]);
942   my $item2   = new_item(qty => 0.5, part => $parts[3]);
943   my $item3   = new_item(qty => 0.5, part => $parts[2]);
944   my $item4   = new_item(qty => 0.5, part => $parts[3]);
945   my $invoice = new_invoice(
946     taxincluded  => 0,
947     invoiceitems => [ $item1, $item2, $item3, $item4 ],
948     payment_id  => $payment_terms->id,
949   );
950   $invoice->post;
951
952   # default values
953   my %params = ( chart_id => $bank_account->chart_id,
954                  transdate => DateTime->today_local->to_kivitendo
955                );
956
957   $params{amount} = $invoice->amount_less_skonto;
958   $params{payment_type} = 'with_skonto_pt';
959
960   $invoice->pay_invoice( %params );
961
962   my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
963   my $total = total_amount($invoice);
964
965   my $title = 'default invoice, four items, 19/7% tax with_skonto_pt 4x25';
966
967   is($invoice->netamount , 100  , "${title}: netamount");
968   is($invoice->amount    , 113  , "${title}: amount");
969   is($paid_amount        , -113 , "${title}: paid amount");
970   is($invoice->paid      , 113  , "${title}: paid");
971   is($number_of_payments , 3    , "${title}: 3 AR_paid bookings");
972   is($total              , 0    , "${title}: even balance");
973 }
974
975 sub test_default_invoice_four_items_19_7_tax_with_skonto_4x_25_tax_included() {
976   reset_state() if $ALWAYS_RESET;
977
978   my $item1   = new_item(qty => 0.5, part => $parts[2]);
979   my $item2   = new_item(qty => 0.5, part => $parts[3]);
980   my $item3   = new_item(qty => 0.5, part => $parts[2]);
981   my $item4   = new_item(qty => 0.5, part => $parts[3]);
982   my $invoice = new_invoice(
983     taxincluded  => 1,
984     invoiceitems => [ $item1, $item2, $item3, $item4 ],
985     payment_id  => $payment_terms->id,
986   );
987   $invoice->post;
988
989   # default values
990   my %params = ( chart_id => $bank_account->chart_id,
991                  transdate => DateTime->today_local->to_kivitendo
992                );
993
994   $params{amount} = $invoice->amount_less_skonto;
995   $params{payment_type} = 'with_skonto_pt';
996
997   $invoice->pay_invoice( %params );
998
999   my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
1000   my $total = total_amount($invoice);
1001
1002   my $title = 'default invoice, four items, 19/7% tax with_skonto_pt 4x25';
1003
1004   is($invoice->netamount,   88.75,    "${title}: netamount");
1005   is($invoice->amount,        100,    "${title}: amount");
1006   is($paid_amount,           -100,    "${title}: paid amount");
1007   is($invoice->paid,          100,    "${title}: paid");
1008   is($number_of_payments,       3,    "${title}: 3 AR_paid bookings");
1009   { local $TODO = "currently this test fails because the code writing the invoice is buggy, the calculation of skonto is correct";
1010   is($total,                    0,    "${title}: even balance");
1011   }
1012 }
1013
1014 sub test_default_invoice_four_items_19_7_tax_with_skonto_4x_25_multiple() {
1015   reset_state() if $ALWAYS_RESET;
1016
1017   my $item1   = new_item(qty => 0.5, part => $parts[2]);
1018   my $item2   = new_item(qty => 0.5, part => $parts[3]);
1019   my $item3   = new_item(qty => 0.5, part => $parts[2]);
1020   my $item4   = new_item(qty => 0.5, part => $parts[3]);
1021   my $invoice = new_invoice(
1022     taxincluded  => 0,
1023     invoiceitems => [ $item1, $item2, $item3, $item4 ],
1024     payment_id  => $payment_terms->id,
1025   );
1026   $invoice->post;
1027
1028   $invoice->pay_invoice( amount       => '90',
1029                          payment_type => 'without_skonto',
1030                          chart_id     => $bank_account->chart_id,
1031                          transdate => DateTime->today_local->to_kivitendo
1032                        );
1033   $invoice->pay_invoice( payment_type => 'difference_as_skonto',
1034                          chart_id     => $bank_account->chart_id,
1035                          transdate    => DateTime->today_local->to_kivitendo
1036                        );
1037
1038   my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
1039   my $total = total_amount($invoice);
1040
1041   my $title = 'default invoice, four items, 19/7% tax with_skonto_pt 4x25';
1042
1043   is($invoice->netamount,  100,     "${title}: netamount");
1044   is($invoice->amount,     113,     "${title}: amount");
1045   is($paid_amount,        -113,     "${title}: paid amount");
1046   is($invoice->paid,       113,     "${title}: paid");
1047   is($number_of_payments,    3,     "${title}: 3 AR_paid bookings");
1048   is($total,                 0,     "${title}: even balance: this will fail due to rounding error in invoice post, not the skonto");
1049 }
1050
1051 sub test_ar_currency_tax_not_included_and_payment {
1052   my $netamount = $::form->round_amount(75 * $exchangerate->sell,2); #  75 in CUR, 100.00 in EUR
1053   my $amount    = $::form->round_amount($netamount * 1.19,2);        # 100 in CUR, 119.00 in EUR
1054   my $invoice   = SL::DB::Invoice->new(
1055       invoice      => 0,
1056       amount       => $amount,
1057       netamount    => $netamount,
1058       transdate    => $transdate1,
1059       taxincluded  => 0,
1060       customer_id  => $customer->id,
1061       taxzone_id   => $customer->taxzone_id,
1062       currency_id  => $currency->id,
1063       transactions => [],
1064       notes        => 'test_ar_currency_tax_not_included_and_payment',
1065   );
1066   $invoice->add_ar_amount_row(
1067     amount     => $invoice->netamount,
1068     chart      => $ar_amount_chart,
1069     tax_id     => $tax->id,
1070   );
1071
1072   $invoice->create_ar_row(chart => $ar_chart);
1073   $invoice->save;
1074
1075   is(SL::DB::Manager::Invoice->get_all_count(where => [ invoice => 0 ]), 1, 'there is one ar transaction');
1076   is($invoice->currency_id , $currency->id , 'currency_id has been saved');
1077   is($invoice->netamount   , 100           , 'ar amount has been converted');
1078   is($invoice->amount      , 119           , 'ar amount has been converted');
1079   is($invoice->taxincluded ,   0           , 'ar transaction doesn\'t have taxincluded');
1080   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');
1081   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');
1082
1083   $invoice->pay_invoice(chart_id   => $bank->id,
1084                         amount     => 50,
1085                         currency   => 'CUR',
1086                         transdate  => $transdate1->to_kivitendo,
1087                        );
1088   $invoice->pay_invoice(chart_id   => $bank->id,
1089                         amount     => 39.25,
1090                         currency   => 'CUR',
1091                         transdate  => $transdate1->to_kivitendo,
1092                        );
1093   # $invoice->pay_invoice(chart_id   => $bank->id,
1094   #                       amount     => 30,
1095   #                       transdate  => $transdate2->to_kivitendo,
1096   #                      );
1097   is(scalar @{$invoice->transactions}, 9, 'ar transaction has 9 transactions (incl. fxtransactions)');
1098   is($invoice->paid, $invoice->amount, 'ar transaction paid = amount in default currency');
1099 };
1100
1101 sub test_ar_currency_tax_included {
1102   # we want the acc_trans amount to be 100
1103   my $amount    = $::form->round_amount(75 * $exchangerate->sell * 1.19);
1104   my $netamount = $::form->round_amount($amount / 1.19,2);
1105   my $invoice = SL::DB::Invoice->new(
1106       invoice      => 0,
1107       amount       => 119,
1108       netamount    => 100,
1109       transdate    => $transdate1,
1110       taxincluded  => 1,
1111       customer_id  => $customer->id,
1112       taxzone_id   => $customer->taxzone_id,
1113       currency_id  => $currency->id,
1114       notes        => 'test_ar_currency_tax_included',
1115       transactions => [],
1116   );
1117   $invoice->add_ar_amount_row( # should take care of taxincluded
1118     amount     => $invoice->amount, # tax included in local currency
1119     chart      => $ar_amount_chart,
1120     tax_id     => $tax->id,
1121   );
1122
1123   $invoice->create_ar_row( chart => $ar_chart );
1124   $invoice->save;
1125   is(SL::DB::Manager::Invoice->get_all_count(where => [ invoice => 0 ]), 2, 'there are now two ar transactions');
1126   is($invoice->currency_id , $currency->id , 'currency_id has been saved');
1127   is($invoice->amount      , $amount       , 'amount ok');
1128   is($invoice->netamount   , $netamount    , 'netamount ok');
1129   is($invoice->taxincluded , 1             , 'ar transaction has taxincluded');
1130   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');
1131   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');
1132   $invoice->pay_invoice(chart_id   => $bank->id,
1133                         amount     => 89.25,
1134                         currency   => 'CUR',
1135                         transdate  => $transdate1->to_kivitendo,
1136                        );
1137
1138 };
1139
1140 sub test_ap_currency_tax_not_included_and_payment {
1141   my $netamount = $::form->round_amount(75 * $exchangerate->buy,2); #  75 in CUR, 100.00 in EUR
1142   my $amount    = $::form->round_amount($netamount * 1.19,2);        # 100 in CUR, 119.00 in EUR
1143   my $invoice   = SL::DB::PurchaseInvoice->new(
1144       invoice      => 0,
1145       invnumber    => 'test_ap_currency_tax_not_included_and_payment',
1146       amount       => $amount,
1147       netamount    => $netamount,
1148       transdate    => $transdate1,
1149       taxincluded  => 0,
1150       vendor_id    => $vendor->id,
1151       taxzone_id   => $vendor->taxzone_id,
1152       currency_id  => $currency->id,
1153       transactions => [],
1154       notes        => 'test_ap_currency_tax_not_included_and_payment',
1155   );
1156   $invoice->add_ap_amount_row(
1157     amount     => $invoice->netamount,
1158     chart      => $ap_amount_chart,
1159     tax_id     => $tax_9->id,
1160   );
1161
1162   $invoice->create_ap_row(chart => $ap_chart);
1163   $invoice->save;
1164
1165   is($invoice->currency_id, $currency->id, 'currency_id has been saved');
1166   is($invoice->netamount, 100, 'ap amount has been converted');
1167   is($invoice->amount, 119, 'ap amount has been converted');
1168   is($invoice->taxincluded, 0, 'ap transaction doesn\'t have taxincluded');
1169   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');
1170   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');
1171
1172   $invoice->pay_invoice(chart_id   => $bank->id,
1173                         amount     => 50,
1174                         currency   => 'CUR',
1175                         transdate  => $transdate1->to_kivitendo,
1176                        );
1177   $invoice->pay_invoice(chart_id   => $bank->id,
1178                         amount     => 39.25,
1179                         currency   => 'CUR',
1180                         transdate  => $transdate1->to_kivitendo,
1181                        );
1182   is(scalar @{$invoice->transactions}, 9, 'ap transaction has 9 transactions (incl. fxtransactions)');
1183   is($invoice->paid, $invoice->amount, 'ap transaction paid = amount in default currency');
1184 };
1185
1186 sub test_ap_currency_tax_included {
1187   # we want the acc_trans amount to be 100
1188   my $amount    = $::form->round_amount(75 * $exchangerate->buy * 1.19);
1189   my $netamount = $::form->round_amount($amount / 1.19,2);
1190   my $invoice = SL::DB::PurchaseInvoice->new(
1191       invoice      => 0,
1192       amount       => 119, #$amount,
1193       netamount    => 100, #$netamount,
1194       transdate    => $transdate1,
1195       taxincluded  => 1,
1196       vendor_id    => $vendor->id,
1197       taxzone_id   => $vendor->taxzone_id,
1198       currency_id  => $currency->id,
1199       notes        => 'test_ap_currency_tax_included',
1200       invnumber    => 'test_ap_currency_tax_included',
1201       transactions => [],
1202   );
1203   $invoice->add_ap_amount_row( # should take care of taxincluded
1204     amount     => $invoice->amount, # tax included in local currency
1205     chart      => $ap_amount_chart,
1206     tax_id     => $tax_9->id,
1207   );
1208
1209   $invoice->create_ap_row( chart => $ap_chart );
1210   $invoice->save;
1211   is($invoice->currency_id , $currency->id , 'currency_id has been saved');
1212   is($invoice->amount      , $amount       , 'amount ok');
1213   is($invoice->netamount   , $netamount    , 'netamount ok');
1214   is($invoice->taxincluded , 1             , 'ap transaction has taxincluded');
1215   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');
1216   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');
1217
1218   $invoice->pay_invoice(chart_id   => $bank->id,
1219                         amount     => 89.25,
1220                         currency   => 'CUR',
1221                         transdate  => $transdate1->to_kivitendo,
1222                        );
1223
1224 };
1225
1226 sub test_ar_currency_tax_not_included_and_payment_2 {
1227   my $title = 'test_ar_currency_tax_not_included_and_payment_2';
1228   my $netamount = $::form->round_amount(125 * $exchangerate2->sell,2); # 125.00 in CUR, 100.00 in EUR
1229   my $amount    = $::form->round_amount($netamount * 1.19,2);          # 148.75 in CUR, 119.00 in EUR
1230   my $invoice   = SL::DB::Invoice->new(
1231       invoice      => 0,
1232       amount       => $amount,
1233       netamount    => $netamount,
1234       transdate    => $transdate2,
1235       taxincluded  => 0,
1236       customer_id  => $customer->id,
1237       taxzone_id   => $customer->taxzone_id,
1238       currency_id  => $currency->id,
1239       transactions => [],
1240       notes        => 'test_ar_currency_tax_not_included_and_payment 0.8',
1241       invnumber    => 'test_ar_currency_tax_not_included_and_payment 0.8',
1242   );
1243   $invoice->add_ar_amount_row(
1244     amount     => $invoice->netamount,
1245     chart      => $ar_amount_chart,
1246     tax_id     => $tax->id,
1247   );
1248
1249   $invoice->create_ar_row(chart => $ar_chart);
1250   $invoice->save;
1251
1252   is($invoice->currency_id , $currency->id , "$title: currency_id has been saved");
1253   is($invoice->netamount   , 100           , "$title: ar amount has been converted");
1254   is($invoice->amount      , 119           , "$title: ar amount has been converted");
1255   is($invoice->taxincluded ,   0           , "$title: ar transaction doesn\"t have taxincluded");
1256   is(SL::DB::Manager::AccTransaction->find_by(chart_id => $ar_amount_chart->id, trans_id => $invoice->id)->amount, '100.00000', $title . " " . $ar_amount_chart->accno . ": has been converted for currency");
1257   is(SL::DB::Manager::AccTransaction->find_by(chart_id => $ar_chart->id, trans_id => $invoice->id)->amount, '-119.00000', $title  . " " . $ar_chart->accno . ': has been converted for currency');
1258
1259   $invoice->pay_invoice(chart_id   => $bank->id,
1260                         amount     => 123.45,
1261                         currency   => 'CUR',
1262                         transdate  => $transdate2->to_kivitendo,
1263                        );
1264   $invoice->pay_invoice(chart_id   => $bank->id,
1265                         amount     => 15.30,
1266                         currency   => 'CUR',
1267                         transdate  => $transdate3->to_kivitendo,
1268                        );
1269   $invoice->pay_invoice(chart_id   => $bank->id,
1270                         amount     => 10.00,
1271                         currency   => 'CUR',
1272                         transdate  => $transdate4->to_kivitendo,
1273                        );
1274   # $invoice->pay_invoice(chart_id   => $bank->id,
1275   #                       amount     => 30,
1276   #                       transdate  => $transdate2->to_kivitendo,
1277   #                      );
1278   my $fx_transactions = SL::DB::Manager::AccTransaction->get_all(where => [ trans_id => $invoice->id, fx_transaction => 1 ], sort_by => ('acc_trans_id'));
1279   is(scalar @{$fx_transactions}, 3, "$title: ar transaction has 3 fx transactions");
1280   is($fx_transactions->[0]->amount, '24.69000', "$title fx transactions 1: 123.45-(123.45*0.8) = 24.69");
1281
1282   is(scalar @{$invoice->transactions}, 14, "$title ar transaction has 14 transactions (incl. fxtransactions and fx_gain)");
1283   is($invoice->paid, $invoice->amount, "$title ar transaction paid = amount in default currency");
1284 };
1285
1286 sub test_ar_currency_tax_not_included_and_payment_2_credit_note {
1287   my $netamount = $::form->round_amount(-125 * $exchangerate2->sell,2); # 125.00 in CUR, 100.00 in EUR
1288   my $amount    = $::form->round_amount($netamount * 1.19,2);          # 148.75 in CUR, 119.00 in EUR
1289   my $invoice   = SL::DB::Invoice->new(
1290       invoice      => 0,
1291       amount       => $amount,
1292       netamount    => $netamount,
1293       transdate    => $transdate2,
1294       taxincluded  => 0,
1295       customer_id  => $customer->id,
1296       taxzone_id   => $customer->taxzone_id,
1297       currency_id  => $currency->id,
1298       transactions => [],
1299       notes        => 'test_ar_currency_tax_not_included_and_payment credit note 0.8',
1300       invnumber    => 'test_ar_currency_tax_not_included_and_payment credit note 0.8',
1301   );
1302   $invoice->add_ar_amount_row(
1303     amount     => $invoice->netamount,
1304     chart      => $ar_amount_chart,
1305     tax_id     => $tax->id,
1306   );
1307
1308   $invoice->create_ar_row(chart => $ar_chart);
1309   $invoice->save;
1310
1311   is($invoice->currency_id , $currency->id , 'currency_id has been saved');
1312   is($invoice->netamount   , -100          , 'ar amount has been converted');
1313   is($invoice->amount      , -119          , 'ar amount has been converted');
1314   is($invoice->taxincluded ,   0           , 'ar transaction doesn\'t have taxincluded');
1315   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');
1316   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');
1317
1318   $invoice->pay_invoice(chart_id   => $bank->id,
1319                         amount     => -123.45,
1320                         currency   => 'CUR',
1321                         transdate  => $transdate2->to_kivitendo,
1322                        );
1323   $invoice->pay_invoice(chart_id   => $bank->id,
1324                         amount     => -25.30,
1325                         currency   => 'CUR',
1326                         transdate  => $transdate2->to_kivitendo,
1327                        );
1328   my $fx_transactions = SL::DB::Manager::AccTransaction->get_all(where => [ trans_id => $invoice->id, fx_transaction => 1 ], sort_by => ('acc_trans_id'));
1329   is(scalar @{$fx_transactions}, 2, 'ar transaction has 2 fx transactions');
1330   is($fx_transactions->[0]->amount, '-24.69000', 'fx transactions 1: 123.45-(123.45*0.8) = 24.69');
1331
1332   is(scalar @{$invoice->transactions}, 9, 'ar transaction has 9 transactions (incl. fxtransactions)');
1333   is($invoice->paid, $invoice->amount, 'ar transaction paid = amount in default currency');
1334 };
1335
1336 sub test_ap_currency_tax_not_included_and_payment_2 {
1337   my $title = 'test_ap_currency_tax_not_included_and_payment_2';
1338   my $netamount = $::form->round_amount(125 * $exchangerate2->sell,2); # 125.00 in CUR, 100.00 in EUR
1339   my $amount    = $::form->round_amount($netamount * 1.19,2);          # 148.75 in CUR, 119.00 in EUR
1340   my $invoice   = SL::DB::PurchaseInvoice->new(
1341       invoice      => 0,
1342       amount       => $amount,
1343       netamount    => $netamount,
1344       transdate    => $transdate2,
1345       taxincluded  => 0,
1346       vendor_id    => $vendor->id,
1347       taxzone_id   => $vendor->taxzone_id,
1348       currency_id  => $currency->id,
1349       transactions => [],
1350       notes        => 'test_ap_currency_tax_not_included_and_payment_2 0.8 + 1.33333',
1351       invnumber    => 'test_ap_currency_tax_not_included_and_payment_2 0.8 + 1.33333',
1352   );
1353   $invoice->add_ap_amount_row(
1354     amount     => $invoice->netamount,
1355     chart      => $ap_amount_chart,
1356     tax_id     => $tax_9->id,
1357   );
1358
1359   $invoice->create_ap_row(chart => $ap_chart);
1360   $invoice->save;
1361
1362   is($invoice->currency_id , $currency->id , "$title: currency_id has been saved");
1363   is($invoice->netamount   ,  100          , "$title: ap amount has been converted");
1364   is($invoice->amount      ,  119          , "$title: ap amount has been converted");
1365   is($invoice->taxincluded ,    0          , "$title: ap transaction doesn\'t have taxincluded");
1366   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');
1367   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');
1368
1369   $invoice->pay_invoice(chart_id   => $bank->id,
1370                         amount     => 10,
1371                         currency   => 'CUR',
1372                         transdate  => $transdate2->to_kivitendo,
1373                        );
1374   $invoice->pay_invoice(chart_id   => $bank->id,
1375                         amount     => 123.45,
1376                         currency   => 'CUR',
1377                         transdate  => $transdate3->to_kivitendo,
1378                        );
1379   $invoice->pay_invoice(chart_id   => $bank->id,
1380                         amount     => 15.30,
1381                         currency   => 'CUR',
1382                         transdate  => $transdate4->to_kivitendo,
1383                        );
1384   my $fx_transactions = SL::DB::Manager::AccTransaction->get_all(where => [ trans_id => $invoice->id, fx_transaction => 1 ], sort_by => ('acc_trans_id'));
1385   is(scalar @{$fx_transactions}, 3, "$title: ap transaction has 3 fx transactions");
1386   is($fx_transactions->[0]->amount,  '-2.00000', "$title: fx transaction 1:  10.00-( 10.00*0.80000) =   2.00000");
1387   is($fx_transactions->[1]->amount,  '68.59000', "$title: fx transaction 2: 123.45-(123.45*1.55557) = -68.58511");
1388   is($fx_transactions->[2]->amount,  '-3.40000', "$title: fx transaction 3:  15.30-(15.30 *0.77777) =   3.40012");
1389
1390   my $fx_loss_transactions = SL::DB::Manager::AccTransaction->get_all(where => [ trans_id => $invoice->id, chart_id => $fxloss_chart->id ], sort_by => ('acc_trans_id'));
1391   my $fx_gain_transactions = SL::DB::Manager::AccTransaction->get_all(where => [ trans_id => $invoice->id, chart_id => $fxgain_chart->id ], sort_by => ('acc_trans_id'));
1392   is($fx_gain_transactions->[0]->amount,   '0.34000', "$title: fx gain amount ok");
1393   is($fx_loss_transactions->[0]->amount, '-93.28000', "$title: fx loss amount ok");
1394
1395   is(scalar @{$invoice->transactions}, 14, "$title: ap transaction has 14 transactions (incl. fxtransactions and gain_loss)");
1396   is($invoice->paid, $invoice->amount, "$title: ap transaction paid = amount in default currency");
1397   is(total_amount($invoice), 0,   "$title: even balance");
1398 };
1399
1400 sub test_ap_currency_tax_not_included_and_payment_2_credit_note {
1401   my $title = 'test_ap_currency_tax_not_included_and_payment_2_credit_note';
1402   my $netamount = $::form->round_amount(-125 * $exchangerate2->sell,2); # 125.00 in CUR, 100.00 in EUR
1403   my $amount    = $::form->round_amount($netamount * 1.19,2);          # 148.75 in CUR, 119.00 in EUR
1404   my $invoice   = SL::DB::PurchaseInvoice->new(
1405       invoice      => 0,
1406       amount       => $amount,
1407       netamount    => $netamount,
1408       transdate    => $transdate2,
1409       taxincluded  => 0,
1410       vendor_id    => $vendor->id,
1411       taxzone_id   => $vendor->taxzone_id,
1412       currency_id  => $currency->id,
1413       transactions => [],
1414       notes        => 'test_ap_currency_tax_not_included_and_payment credit note 0.8 + 1.33333',
1415       invnumber    => 'test_ap_currency_tax_not_included_and_payment credit note 0.8 + 1.33333',
1416   );
1417   $invoice->add_ap_amount_row(
1418     amount     => $invoice->netamount,
1419     chart      => $ap_amount_chart,
1420     tax_id     => $tax_9->id,
1421   );
1422
1423   $invoice->create_ap_row(chart => $ap_chart);
1424   $invoice->save;
1425
1426   is($invoice->currency_id , $currency->id , "$title: currency_id has been saved");
1427   is($invoice->netamount   , -100          , "$title: ap amount has been converted");
1428   is($invoice->amount      , -119          , "$title: ap amount has been converted");
1429   is($invoice->taxincluded ,   0           , "$title: ap transaction doesn\'t have taxincluded");
1430   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');
1431   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');
1432
1433   $invoice->pay_invoice(chart_id   => $bank->id,
1434                         amount     => -10,
1435                         currency   => 'CUR',
1436                         transdate  => $transdate2->to_kivitendo,
1437                        );
1438   $invoice->pay_invoice(chart_id   => $bank->id,
1439                         amount     => -123.45,
1440                         currency   => 'CUR',
1441                         transdate  => $transdate3->to_kivitendo,
1442                        );
1443   $invoice->pay_invoice(chart_id   => $bank->id,
1444                         amount     => -15.30,
1445                         currency   => 'CUR',
1446                         transdate  => $transdate4->to_kivitendo,
1447                        );
1448   my $fx_transactions = SL::DB::Manager::AccTransaction->get_all(where => [ trans_id => $invoice->id, fx_transaction => 1 ], sort_by => ('acc_trans_id'));
1449   is(scalar @{$fx_transactions}, 3, "$title: ap transaction has 3 fx transactions");
1450   is($fx_transactions->[0]->amount,   '2.00000', "$title: fx transaction 1:  10.00-( 10.00*0.80000) =   2.00000");
1451   is($fx_transactions->[1]->amount, '-68.59000', "$title: fx transaction 2: 123.45-(123.45*1.55557) = -68.58511");
1452   is($fx_transactions->[2]->amount,   '3.40000', "$title: fx transaction 3:  15.30-(15.30 *0.77777) =   3.40012");
1453
1454   my $fx_gain_loss_transactions = SL::DB::Manager::AccTransaction->get_all(where => [ trans_id => $invoice->id, chart_id => $fxgain_chart->id ], sort_by => ('acc_trans_id'));
1455   is($fx_gain_loss_transactions->[0]->amount, '93.28000', "$title: fx gain loss amount ok");
1456
1457   is(scalar @{$invoice->transactions}, 14, "$title: ap transaction has 14 transactions (incl. fxtransactions and gain_loss)");
1458   is($invoice->paid, $invoice->amount, "$title: ap transaction paid = amount in default currency");
1459   is(total_amount($invoice), 0,   "$title: even balance");
1460 };
1461
1462 Support::TestSetup::login();
1463
1464 # test cases: without_skonto
1465 test_default_invoice_one_item_19_without_skonto();
1466 test_default_invoice_two_items_19_7_tax_with_skonto();
1467 test_default_invoice_two_items_19_7_without_skonto();
1468 test_default_invoice_two_items_19_7_without_skonto_incomplete_payment();
1469 test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments();
1470 test_default_purchase_invoice_two_charts_19_7_without_skonto();
1471 test_default_purchase_invoice_two_charts_19_7_tax_partial_unrounded_payment_without_skonto();
1472 test_default_invoice_one_item_19_without_skonto_overpaid();
1473
1474 # test cases: difference_as_skonto
1475 test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto();
1476 test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto_1cent();
1477 test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto_2cent();
1478 test_default_invoice_one_item_19_multiple_payment_final_difference_as_skonto();
1479 test_default_invoice_one_item_19_multiple_payment_final_difference_as_skonto_1cent();
1480 test_default_purchase_invoice_two_charts_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto();
1481
1482 # test cases: with_skonto_pt
1483 test_default_invoice_two_items_19_7_tax_with_skonto_50_50();
1484 test_default_invoice_four_items_19_7_tax_with_skonto_4x_25();
1485 test_default_invoice_four_items_19_7_tax_with_skonto_4x_25_multiple();
1486 test_default_purchase_invoice_two_charts_19_7_with_skonto();
1487 test_default_invoice_four_items_19_7_tax_with_skonto_4x_25_tax_included();
1488 test_default_invoice_two_items_19_7_tax_with_skonto_tax_included();
1489
1490 # test payment of ar and ap transactions with currency and tax included/not included
1491 # exchangerate = 1.33333
1492 test_ar_currency_tax_not_included_and_payment();
1493 test_ar_currency_tax_included();
1494 test_ap_currency_tax_not_included_and_payment();
1495 test_ap_currency_tax_included();
1496
1497 test_ar_currency_tax_not_included_and_payment_2();              # exchangerate 0.8
1498 test_ar_currency_tax_not_included_and_payment_2_credit_note();  # exchangerate 0.8
1499
1500 test_ap_currency_tax_not_included_and_payment_2();             # two exchangerates, with fx_gain_loss
1501 test_ap_currency_tax_not_included_and_payment_2_credit_note(); # two exchangerates, with fx_gain_loss
1502
1503 { local $TODO = "currently this test fails because the code writing the invoice is buggy, the calculation of skonto is correct";
1504   my ($acc_trans_sum)  = selectfirst_array_query($::form, $currency->db->dbh, 'SELECT SUM(amount) FROM acc_trans'); is($acc_trans_sum, '0.00000', "sum of all acc_trans is 0");
1505 }
1506
1507 # remove all created data at end of test
1508 clear_up();
1509
1510 done_testing();
1511
1512 1;