9 use Support::TestSetup;
11 use List::Util qw(sum);
13 use SL::DB::Buchungsgruppe;
15 use SL::DB::Exchangerate;
23 use SL::DB::BankAccount;
24 use SL::DB::PaymentTerm;
25 use SL::DBUtils qw(selectfirst_array_query);
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);
34 my $reset_state_counter = 0;
36 my $purchase_invoice_counter = 0; # used for generating purchase invnumber
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' ]);
54 return if $reset_state_counter;
56 $params{$_} ||= {} for qw(buchungsgruppe unit customer part tax vendor);
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);
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\%";
75 $currency_id = $::instance_conf->get_currency_id;
77 $currency = SL::DB::Currency->new(name => 'CUR')->save;
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";
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");
88 $exchangerate = SL::DB::Exchangerate->new(transdate => $transdate1,
91 currency_id => $currency->id,
93 $exchangerate2 = SL::DB::Exchangerate->new(transdate => $transdate2,
96 currency_id => $currency->id,
98 $exchangerate3 = SL::DB::Exchangerate->new(transdate => $transdate3,
101 currency_id => $currency->id,
103 $exchangerate4 = SL::DB::Exchangerate->new(transdate => $transdate4,
106 currency_id => $currency->id,
109 $customer = SL::DB::Customer->new(
110 name => 'Test Customer',
111 currency_id => $currency_id,
112 taxzone_id => $taxzone->id,
113 %{ $params{customer} }
116 $bank_account = SL::DB::BankAccount->new(
117 account_number => '123',
122 chart_id => SL::DB::Manager::Chart->find_by( description => 'Bank' )->id,
123 name => SL::DB::Manager::Chart->find_by( description => 'Bank' )->description,
126 $payment_terms = SL::DB::PaymentTerm->new(
127 description => 'payment',
128 description_long => 'payment',
131 percent_skonto => '0.05',
132 auto_calculation => 1,
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,
145 push @parts, SL::DB::Part->new(
146 partnumber => 'T4254',
147 description => 'Fourty-two fifty-four',
151 buchungsgruppen_id => $buchungsgruppe->id,
156 push @parts, SL::DB::Part->new(
157 partnumber => 'T0815',
158 description => 'Zero EIGHT fifteeN @ 7%',
162 buchungsgruppen_id => $buchungsgruppe7->id,
166 push @parts, SL::DB::Part->new(
168 description => 'Testware 19%',
172 buchungsgruppen_id => $buchungsgruppe->id,
176 push @parts, SL::DB::Part->new(
178 description => 'Testware 7%',
182 buchungsgruppen_id => $buchungsgruppe7->id,
187 $ar_chart = SL::DB::Manager::Chart->find_by( accno => '1400' ); # Forderungen
188 $ap_chart = SL::DB::Manager::Chart->find_by( accno => '1600' ); # Verbindlichkeiten
189 $bank = SL::DB::Manager::Chart->find_by( accno => '1200' ); # Bank
190 $ar_amount_chart = SL::DB::Manager::Chart->find_by( accno => '8400' ); # Erlöse
191 $ap_amount_chart = SL::DB::Manager::Chart->find_by( accno => '3400' ); # Wareneingang 19%
193 $reset_state_counter++;
199 return SL::DB::Invoice->new(
200 customer_id => $customer->id,
201 currency_id => $currency_id,
202 employee_id => $employee->id,
203 salesman_id => $employee->id,
204 gldate => $transdate1,
205 taxzone_id => $taxzone->id,
206 transdate => $transdate1,
214 sub new_purchase_invoice {
216 # manually create a Kreditorenbuchung from scratch, ap + acc_trans bookings, as no helper exists yet, like $invoice->post.
217 # arap-Booking must come last in the acc_trans order
218 $purchase_invoice_counter++;
220 my $purchase_invoice = SL::DB::PurchaseInvoice->new(
221 vendor_id => $vendor->id,
222 invnumber => 'newap ' . $purchase_invoice_counter ,
223 currency_id => $currency_id,
224 employee_id => $employee->id,
225 gldate => $transdate1,
226 taxzone_id => $taxzone->id,
227 transdate => $transdate1,
237 my $expense_chart = SL::DB::Manager::Chart->find_by(accno => '3400');
238 my $expense_chart_booking= SL::DB::AccTransaction->new(
239 trans_id => $purchase_invoice->id,
240 chart_id => $expense_chart->id,
241 chart_link => $expense_chart->link,
243 transdate => $transdate1,
246 tax_id => SL::DB::Manager::Tax->find_by(taxkey => 9)->id);
247 $expense_chart_booking->save;
249 my $tax_chart = SL::DB::Manager::Chart->find_by(accno => '1576');
250 my $tax_chart_booking= SL::DB::AccTransaction->new(
251 trans_id => $purchase_invoice->id,
252 chart_id => $tax_chart->id,
253 chart_link => $tax_chart->link,
255 transdate => $transdate1,
258 tax_id => SL::DB::Manager::Tax->find_by(taxkey => 9)->id);
259 $tax_chart_booking->save;
260 $expense_chart = SL::DB::Manager::Chart->find_by(accno => '3300');
261 $expense_chart_booking= SL::DB::AccTransaction->new(
262 trans_id => $purchase_invoice->id,
263 chart_id => $expense_chart->id,
264 chart_link => $expense_chart->link,
266 transdate => $transdate1,
269 tax_id => SL::DB::Manager::Tax->find_by(taxkey => 8)->id);
270 $expense_chart_booking->save;
273 $tax_chart = SL::DB::Manager::Chart->find_by(accno => '1571');
274 $tax_chart_booking= SL::DB::AccTransaction->new(
275 trans_id => $purchase_invoice->id,
276 chart_id => $tax_chart->id,
277 chart_link => $tax_chart->link,
279 transdate => $transdate1,
282 tax_id => SL::DB::Manager::Tax->find_by(taxkey => 8)->id);
283 $tax_chart_booking->save;
284 my $arap_chart = SL::DB::Manager::Chart->find_by(accno => '1600');
285 my $arap_booking= SL::DB::AccTransaction->new(trans_id => $purchase_invoice->id,
286 chart_id => $arap_chart->id,
287 chart_link => $arap_chart->link,
289 transdate => $transdate1,
292 tax_id => SL::DB::Manager::Tax->find_by(taxkey => 0)->id);
295 return $purchase_invoice;
301 my $part = delete($params{part}) || $parts[0];
303 return SL::DB::InvoiceItem->new(
304 parts_id => $part->id,
305 lastcost => $part->lastcost,
306 sellprice => $part->sellprice,
307 description => $part->description,
313 sub number_of_payments {
316 my $number_of_payments;
318 foreach my $transaction ( @{ $invoice->transactions } ) {
319 if ( $transaction->chart_link =~ /(AR_paid|AP_paid)/ ) {
320 $paid_amount += $transaction->amount ;
321 $number_of_payments++;
324 return ($number_of_payments, $paid_amount);
330 my $total = sum map { $_->amount } @{ $invoice->transactions };
332 return $::form->round_amount($total, 5);
338 sub test_default_invoice_one_item_19_without_skonto() {
339 reset_state() if $ALWAYS_RESET;
341 my $item = new_item(qty => 2.5);
342 my $invoice = new_invoice(
344 invoiceitems => [ $item ],
345 payment_id => $payment_terms->id,
349 my $purchase_invoice = new_purchase_invoice();
353 my %params = ( chart_id => $bank_account->chart_id,
354 transdate => DateTime->today_local->to_kivitendo
357 $params{amount} = '6.96';
358 $params{payment_type} = 'without_skonto';
360 $invoice->pay_invoice( %params );
362 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
363 my $total = total_amount($invoice);
365 my $title = 'default invoice, one item, 19% tax, without_skonto';
367 is($invoice->netamount, 5.85, "${title}: netamount");
368 is($invoice->amount, 6.96, "${title}: amount");
369 is($paid_amount, -6.96, "${title}: paid amount");
370 is($number_of_payments, 1, "${title}: 1 AR_paid booking");
371 is($invoice->paid, 6.96, "${title}: paid");
372 is($total, 0, "${title}: even balance");
376 sub test_default_invoice_one_item_19_without_skonto_overpaid() {
377 reset_state() if $ALWAYS_RESET;
379 my $item = new_item(qty => 2.5);
380 my $invoice = new_invoice(
382 invoiceitems => [ $item ],
383 payment_id => $payment_terms->id,
387 my $purchase_invoice = new_purchase_invoice();
391 my %params = ( chart_id => $bank_account->chart_id,
392 transdate => DateTime->today_local->to_kivitendo
395 $params{amount} = '16.96';
396 $params{payment_type} = 'without_skonto';
397 $invoice->pay_invoice( %params );
399 $params{amount} = '-10.00';
400 $invoice->pay_invoice( %params );
402 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
403 my $total = total_amount($invoice);
405 my $title = 'default invoice, one item, 19% tax, without_skonto';
407 is($invoice->netamount, 5.85, "${title}: netamount");
408 is($invoice->amount, 6.96, "${title}: amount");
409 is($paid_amount, -6.96, "${title}: paid amount");
410 is($number_of_payments, 2, "${title}: 1 AR_paid booking");
411 is($invoice->paid, 6.96, "${title}: paid");
412 is($total, 0, "${title}: even balance");
418 sub test_default_invoice_two_items_19_7_tax_with_skonto() {
419 reset_state() if $ALWAYS_RESET;
421 my $item1 = new_item(qty => 2.5);
422 my $item2 = new_item(qty => 1.2, part => $parts[1]);
423 my $invoice = new_invoice(
425 invoiceitems => [ $item1, $item2 ],
426 payment_id => $payment_terms->id,
431 my %params = ( chart_id => $bank_account->chart_id,
432 transdate => DateTime->today_local->to_kivitendo
435 $params{payment_type} = 'with_skonto_pt';
436 $params{amount} = $invoice->amount_less_skonto;
438 $invoice->pay_invoice( %params );
440 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
441 my $total = total_amount($invoice);
443 my $title = 'default invoice, two items, 19/7% tax with_skonto_pt';
445 is($invoice->netamount, 5.85 + 11.66, "${title}: netamount");
446 is($invoice->amount, 6.96 + 12.48, "${title}: amount");
447 is($paid_amount, -19.44, "${title}: paid amount");
448 is($invoice->paid, 19.44, "${title}: paid");
449 is($number_of_payments, 3, "${title}: 3 AR_paid bookings");
450 is($total, 0, "${title}: even balance");
453 sub test_default_invoice_two_items_19_7_tax_with_skonto_tax_included() {
454 reset_state() if $ALWAYS_RESET;
456 my $item1 = new_item(qty => 2.5);
457 my $item2 = new_item(qty => 1.2, part => $parts[1]);
458 my $invoice = new_invoice(
460 invoiceitems => [ $item1, $item2 ],
461 payment_id => $payment_terms->id,
466 my %params = ( chart_id => $bank_account->chart_id,
467 transdate => DateTime->today_local->to_kivitendo
470 $params{payment_type} = 'with_skonto_pt';
471 $params{amount} = $invoice->amount_less_skonto;
473 $invoice->pay_invoice( %params );
475 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
476 my $total = total_amount($invoice);
478 my $title = 'default invoice, two items, 19/7% tax with_skonto_pt';
480 is($invoice->netamount, 15.82, "${title}: netamount");
481 is($invoice->amount, 17.51, "${title}: amount");
482 is($paid_amount, -17.51, "${title}: paid amount");
483 is($invoice->paid, 17.51, "${title}: paid");
484 is($number_of_payments, 3, "${title}: 3 AR_paid bookings");
485 { local $TODO = "currently this test fails because the code writing the invoice is buggy, the calculation of skonto is correct";
486 is($total, 0, "${title}: even balance");
490 # test 3 : two items, without skonto
491 sub test_default_invoice_two_items_19_7_without_skonto() {
492 reset_state() if $ALWAYS_RESET;
494 my $item1 = new_item(qty => 2.5);
495 my $item2 = new_item(qty => 1.2, part => $parts[1]);
496 my $invoice = new_invoice(
498 invoiceitems => [ $item1, $item2 ],
499 payment_id => $payment_terms->id,
504 my %params = ( chart_id => $bank_account->chart_id,
505 transdate => DateTime->today_local->to_kivitendo
508 $params{amount} = '19.44'; # pass full amount
509 $params{payment_type} = 'without_skonto';
511 $invoice->pay_invoice( %params );
513 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
514 my $total = total_amount($invoice);
516 my $title = 'default invoice, two items, 19/7% tax without skonto';
518 is($invoice->netamount, 5.85 + 11.66, "${title}: netamount");
519 is($invoice->amount, 6.96 + 12.48, "${title}: amount");
520 is($paid_amount, -19.44, "${title}: paid amount");
521 is($invoice->paid, 19.44, "${title}: paid");
522 is($number_of_payments, 1, "${title}: 1 AR_paid bookings");
523 is($total, 0, "${title}: even balance");
527 sub test_default_invoice_two_items_19_7_without_skonto_incomplete_payment() {
528 reset_state() if $ALWAYS_RESET;
530 my $item1 = new_item(qty => 2.5);
531 my $item2 = new_item(qty => 1.2, part => $parts[1]);
532 my $invoice = new_invoice(
534 invoiceitems => [ $item1, $item2 ],
535 payment_id => $payment_terms->id,
539 $invoice->pay_invoice( amount => '9.44',
540 payment_type => 'without_skonto',
541 chart_id => $bank_account->chart_id,
542 transdate => DateTime->today_local->to_kivitendo,
545 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
546 my $total = total_amount($invoice);
548 my $title = 'default invoice, two items, 19/7% tax without skonto incomplete payment';
550 is($invoice->netamount, 5.85 + 11.66, "${title}: netamount");
551 is($invoice->amount, 6.96 + 12.48, "${title}: amount");
552 is($paid_amount, -9.44, "${title}: paid amount");
553 is($invoice->paid, 9.44, "${title}: paid");
554 is($number_of_payments, 1, "${title}: 1 AR_paid bookings");
555 is($total, 0, "${title}: even balance");
559 sub test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments() {
560 reset_state() if $ALWAYS_RESET;
562 my $item1 = new_item(qty => 2.5);
563 my $item2 = new_item(qty => 1.2, part => $parts[1]);
564 my $invoice = new_invoice(
566 invoiceitems => [ $item1, $item2 ],
567 payment_id => $payment_terms->id,
571 $invoice->pay_invoice( amount => '9.44',
572 payment_type => 'without_skonto',
573 chart_id => $bank_account->chart_id,
574 transdate => DateTime->today_local->to_kivitendo
576 $invoice->pay_invoice( amount => '10.00',
577 chart_id => $bank_account->chart_id,
578 transdate => DateTime->today_local->to_kivitendo
581 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
582 my $total = total_amount($invoice);
584 my $title = 'default invoice, two items, 19/7% tax not included';
586 is($invoice->netamount, 5.85 + 11.66, "${title}: netamount");
587 is($invoice->amount, 6.96 + 12.48, "${title}: amount");
588 is($paid_amount, -19.44, "${title}: paid amount");
589 is($invoice->paid, 19.44, "${title}: paid");
590 is($number_of_payments, 2, "${title}: 2 AR_paid bookings");
591 is($total, 0, "${title}: even balance");
596 sub test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto() {
597 reset_state() if $ALWAYS_RESET;
599 my $item1 = new_item(qty => 2.5);
600 my $item2 = new_item(qty => 1.2, part => $parts[1]);
601 my $invoice = new_invoice(
603 invoiceitems => [ $item1, $item2 ],
604 payment_id => $payment_terms->id,
608 $invoice->pay_invoice( amount => '9.44',
609 payment_type => 'without_skonto',
610 chart_id => $bank_account->chart_id,
611 transdate => DateTime->today_local->to_kivitendo
613 $invoice->pay_invoice( amount => '8.73',
614 payment_type => 'without_skonto',
615 chart_id => $bank_account->chart_id,
616 transdate => DateTime->today_local->to_kivitendo
618 $invoice->pay_invoice( amount => $invoice->open_amount,
619 payment_type => 'difference_as_skonto',
620 chart_id => $bank_account->chart_id,
621 transdate => DateTime->today_local->to_kivitendo
624 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
625 my $total = total_amount($invoice);
627 my $title = 'default invoice, two items, 19/7% tax not included';
629 is($invoice->netamount, 5.85 + 11.66, "${title}: netamount");
630 is($invoice->amount, 6.96 + 12.48, "${title}: amount");
631 is($paid_amount, -19.44, "${title}: paid amount");
632 is($invoice->paid, 19.44, "${title}: paid");
633 is($number_of_payments, 4, "${title}: 4 AR_paid bookings");
634 is($total, 0, "${title}: even balance");
638 sub test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto_1cent() {
639 reset_state() if $ALWAYS_RESET;
641 # if there is only one cent left there can only be one skonto booking, the
642 # error handling should choose the highest amount, which is the 7% account
643 # (11.66) rather than the 19% account (5.85). The actual tax amount is
644 # higher for the 19% case, though (1.11 compared to 0.82)
646 my $item1 = new_item(qty => 2.5);
647 my $item2 = new_item(qty => 1.2, part => $parts[1]);
648 my $invoice = new_invoice(
650 invoiceitems => [ $item1, $item2 ],
651 payment_id => $payment_terms->id,
655 $invoice->pay_invoice( amount => '19.42',
656 payment_type => 'without_skonto',
657 chart_id => $bank_account->chart_id,
658 transdate => DateTime->today_local->to_kivitendo
660 $invoice->pay_invoice( amount => $invoice->open_amount,
661 payment_type => 'difference_as_skonto',
662 chart_id => $bank_account->chart_id,
663 transdate => DateTime->today_local->to_kivitendo
666 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
667 my $total = total_amount($invoice);
669 my $title = 'default invoice, two items, 19/7% tax not included';
671 is($invoice->netamount, 5.85 + 11.66, "${title}: netamount");
672 is($invoice->amount, 6.96 + 12.48, "${title}: amount");
673 is($paid_amount, -19.44, "${title}: paid amount");
674 is($invoice->paid, 19.44, "${title}: paid");
675 is($number_of_payments, 3, "${title}: 2 AR_paid bookings");
676 is($total, 0, "${title}: even balance");
680 sub test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto_2cent() {
681 reset_state() if $ALWAYS_RESET;
683 # if there are two cents left there will be two skonto bookings, 1 cent each
684 my $item1 = new_item(qty => 2.5);
685 my $item2 = new_item(qty => 1.2, part => $parts[1]);
686 my $invoice = new_invoice(
688 invoiceitems => [ $item1, $item2 ],
689 payment_id => $payment_terms->id,
693 $invoice->pay_invoice( amount => '19.42',
694 payment_type => 'without_skonto',
695 chart_id => $bank_account->chart_id,
696 transdate => DateTime->today_local->to_kivitendo
698 $invoice->pay_invoice( amount => $invoice->open_amount,
699 payment_type => 'difference_as_skonto',
700 chart_id => $bank_account->chart_id,
701 transdate => DateTime->today_local->to_kivitendo
704 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
705 my $total = total_amount($invoice);
707 my $title = 'default invoice, two items, 19/7% tax not included';
709 is($invoice->netamount, 5.85 + 11.66, "${title}: netamount");
710 is($invoice->amount, 6.96 + 12.48, "${title}: amount");
711 is($paid_amount, -19.44, "${title}: paid amount");
712 is($invoice->paid, 19.44, "${title}: paid");
713 is($number_of_payments, 3, "${title}: 3 AR_paid bookings");
714 is($total, 0, "${title}: even balance");
718 sub test_default_invoice_one_item_19_multiple_payment_final_difference_as_skonto() {
719 reset_state() if $ALWAYS_RESET;
721 my $item = new_item(qty => 2.5);
722 my $invoice = new_invoice(
724 invoiceitems => [ $item ],
725 payment_id => $payment_terms->id,
730 my %params = ( chart_id => $bank_account->chart_id,
731 transdate => DateTime->today_local->to_kivitendo
734 $params{amount} = '2.32';
735 $params{payment_type} = 'without_skonto';
736 $invoice->pay_invoice( %params );
738 $params{amount} = '3.81';
739 $params{payment_type} = 'without_skonto';
740 $invoice->pay_invoice( %params );
742 $params{amount} = $invoice->open_amount; # set amount, otherwise previous 3.81 is used
743 $params{payment_type} = 'difference_as_skonto';
744 $invoice->pay_invoice( %params );
746 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
747 my $total = total_amount($invoice);
749 my $title = 'default invoice, one item, 19% tax, without_skonto';
751 is($invoice->netamount, 5.85, "${title}: netamount");
752 is($invoice->amount, 6.96, "${title}: amount");
753 is($paid_amount, -6.96, "${title}: paid amount");
754 is($number_of_payments, 3, "${title}: 3 AR_paid booking");
755 is($invoice->paid, 6.96, "${title}: paid");
756 is($total, 0, "${title}: even balance");
760 sub test_default_invoice_one_item_19_multiple_payment_final_difference_as_skonto_1cent() {
761 reset_state() if $ALWAYS_RESET;
763 my $item = new_item(qty => 2.5);
764 my $invoice = new_invoice(
766 invoiceitems => [ $item ],
767 payment_id => $payment_terms->id,
772 my %params = ( chart_id => $bank_account->chart_id,
773 transdate => DateTime->today_local->to_kivitendo
776 $params{amount} = '6.95';
777 $params{payment_type} = 'without_skonto';
778 $invoice->pay_invoice( %params );
780 $params{amount} = $invoice->open_amount; # set amount, otherwise previous value 6.95 is used
781 $params{payment_type} = 'difference_as_skonto';
782 $invoice->pay_invoice( %params );
784 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
785 my $total = total_amount($invoice);
787 my $title = 'default invoice, one item, 19% tax, without_skonto';
789 is($invoice->netamount, 5.85, "${title}: netamount");
790 is($invoice->amount, 6.96, "${title}: amount");
791 is($paid_amount, -6.96, "${title}: paid amount");
792 is($number_of_payments, 2, "${title}: 3 AR_paid booking");
793 is($invoice->paid, 6.96, "${title}: paid");
794 is($total, 0, "${title}: even balance");
798 # test 3 : two items, without skonto
799 sub test_default_purchase_invoice_two_charts_19_7_without_skonto() {
800 reset_state() if $ALWAYS_RESET;
802 my $purchase_invoice = new_purchase_invoice();
804 my %params = ( chart_id => $bank_account->chart_id,
805 transdate => DateTime->today_local->to_kivitendo
808 $params{amount} = '226'; # pass full amount
809 $params{payment_type} = 'without_skonto';
811 $purchase_invoice->pay_invoice( %params );
813 my ($number_of_payments, $paid_amount) = number_of_payments($purchase_invoice);
814 my $total = total_amount($purchase_invoice);
816 my $title = 'default invoice, two items, 19/7% tax without skonto';
818 is($paid_amount, 226, "${title}: paid amount");
819 is($number_of_payments, 1, "${title}: 1 AP_paid bookings");
820 is($total, 0, "${title}: even balance");
824 sub test_default_purchase_invoice_two_charts_19_7_with_skonto() {
825 reset_state() if $ALWAYS_RESET;
827 my $purchase_invoice = new_purchase_invoice();
829 my %params = ( chart_id => $bank_account->chart_id,
830 transdate => DateTime->today_local->to_kivitendo
833 # $params{amount} = '226'; # pass full amount
834 $params{payment_type} = 'with_skonto_pt';
836 $purchase_invoice->pay_invoice( %params );
838 my ($number_of_payments, $paid_amount) = number_of_payments($purchase_invoice);
839 my $total = total_amount($purchase_invoice);
841 my $title = 'default invoice, two items, 19/7% tax without skonto';
843 is($paid_amount, 226, "${title}: paid amount");
844 is($number_of_payments, 3, "${title}: 1 AP_paid bookings");
845 is($total, 0, "${title}: even balance");
849 sub test_default_purchase_invoice_two_charts_19_7_tax_partial_unrounded_payment_without_skonto() {
850 # check whether unrounded amounts passed via $params{amount} are rounded for without_skonto case
851 reset_state() if $ALWAYS_RESET;
852 my $purchase_invoice = new_purchase_invoice();
853 $purchase_invoice->pay_invoice(
854 amount => ( $purchase_invoice->amount / 3 * 2),
855 payment_type => 'without_skonto',
856 chart_id => $bank_account->chart_id,
857 transdate => DateTime->today_local->to_kivitendo
859 my ($number_of_payments, $paid_amount) = number_of_payments($purchase_invoice);
860 my $total = total_amount($purchase_invoice);
862 my $title = 'default purchase_invoice, two charts, 19/7% tax multiple payments with final difference as skonto';
864 is($paid_amount, 150.67, "${title}: paid amount");
865 is($number_of_payments, 1, "${title}: 1 AP_paid bookings");
866 is($total, 0, "${title}: even balance");
870 sub test_default_purchase_invoice_two_charts_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto() {
871 reset_state() if $ALWAYS_RESET;
873 my $purchase_invoice = new_purchase_invoice();
875 # pay 2/3 and 1/5, leaves 3.83% to be used as Skonto
876 $purchase_invoice->pay_invoice(
877 amount => ( $purchase_invoice->amount / 3 * 2),
878 payment_type => 'without_skonto',
879 chart_id => $bank_account->chart_id,
880 transdate => DateTime->today_local->to_kivitendo
882 $purchase_invoice->pay_invoice(
883 amount => ( $purchase_invoice->amount / 5 ),
884 payment_type => 'without_skonto',
885 chart_id => $bank_account->chart_id,
886 transdate => DateTime->today_local->to_kivitendo
888 $purchase_invoice->pay_invoice(
889 payment_type => 'difference_as_skonto',
890 chart_id => $bank_account->chart_id,
891 transdate => DateTime->today_local->to_kivitendo
894 my ($number_of_payments, $paid_amount) = number_of_payments($purchase_invoice);
895 my $total = total_amount($purchase_invoice);
897 my $title = 'default purchase_invoice, two charts, 19/7% tax multiple payments with final difference as skonto';
899 is($paid_amount, 226, "${title}: paid amount");
900 is($number_of_payments, 4, "${title}: 1 AP_paid bookings");
901 is($total, 0, "${title}: even balance");
906 sub test_default_invoice_two_items_19_7_tax_with_skonto_50_50() {
907 reset_state() if $ALWAYS_RESET;
909 my $item1 = new_item(qty => 1, part => $parts[2]);
910 my $item2 = new_item(qty => 1, part => $parts[3]);
911 my $invoice = new_invoice(
913 invoiceitems => [ $item1, $item2 ],
914 payment_id => $payment_terms->id,
919 my %params = ( chart_id => $bank_account->chart_id,
920 transdate => DateTime->today_local->to_kivitendo
923 $params{amount} = $invoice->amount_less_skonto;
924 $params{payment_type} = 'with_skonto_pt';
926 $invoice->pay_invoice( %params );
928 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
929 my $total = total_amount($invoice);
931 my $title = 'default invoice, two items, 19/7% tax with_skonto_pt 50/50';
933 is($invoice->netamount, 100, "${title}: netamount");
934 is($invoice->amount, 113, "${title}: amount");
935 is($paid_amount, -113, "${title}: paid amount");
936 is($invoice->paid, 113, "${title}: paid");
937 is($number_of_payments, 3, "${title}: 3 AR_paid bookings");
938 is($total, 0, "${title}: even balance");
942 sub test_default_invoice_four_items_19_7_tax_with_skonto_4x_25() {
943 reset_state() if $ALWAYS_RESET;
945 my $item1 = new_item(qty => 0.5, part => $parts[2]);
946 my $item2 = new_item(qty => 0.5, part => $parts[3]);
947 my $item3 = new_item(qty => 0.5, part => $parts[2]);
948 my $item4 = new_item(qty => 0.5, part => $parts[3]);
949 my $invoice = new_invoice(
951 invoiceitems => [ $item1, $item2, $item3, $item4 ],
952 payment_id => $payment_terms->id,
957 my %params = ( chart_id => $bank_account->chart_id,
958 transdate => DateTime->today_local->to_kivitendo
961 $params{amount} = $invoice->amount_less_skonto;
962 $params{payment_type} = 'with_skonto_pt';
964 $invoice->pay_invoice( %params );
966 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
967 my $total = total_amount($invoice);
969 my $title = 'default invoice, four items, 19/7% tax with_skonto_pt 4x25';
971 is($invoice->netamount , 100 , "${title}: netamount");
972 is($invoice->amount , 113 , "${title}: amount");
973 is($paid_amount , -113 , "${title}: paid amount");
974 is($invoice->paid , 113 , "${title}: paid");
975 is($number_of_payments , 3 , "${title}: 3 AR_paid bookings");
976 is($total , 0 , "${title}: even balance");
979 sub test_default_invoice_four_items_19_7_tax_with_skonto_4x_25_tax_included() {
980 reset_state() if $ALWAYS_RESET;
982 my $item1 = new_item(qty => 0.5, part => $parts[2]);
983 my $item2 = new_item(qty => 0.5, part => $parts[3]);
984 my $item3 = new_item(qty => 0.5, part => $parts[2]);
985 my $item4 = new_item(qty => 0.5, part => $parts[3]);
986 my $invoice = new_invoice(
988 invoiceitems => [ $item1, $item2, $item3, $item4 ],
989 payment_id => $payment_terms->id,
994 my %params = ( chart_id => $bank_account->chart_id,
995 transdate => DateTime->today_local->to_kivitendo
998 $params{amount} = $invoice->amount_less_skonto;
999 $params{payment_type} = 'with_skonto_pt';
1001 $invoice->pay_invoice( %params );
1003 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
1004 my $total = total_amount($invoice);
1006 my $title = 'default invoice, four items, 19/7% tax with_skonto_pt 4x25';
1008 is($invoice->netamount, 88.75, "${title}: netamount");
1009 is($invoice->amount, 100, "${title}: amount");
1010 is($paid_amount, -100, "${title}: paid amount");
1011 is($invoice->paid, 100, "${title}: paid");
1012 is($number_of_payments, 3, "${title}: 3 AR_paid bookings");
1013 { local $TODO = "currently this test fails because the code writing the invoice is buggy, the calculation of skonto is correct";
1014 is($total, 0, "${title}: even balance");
1018 sub test_default_invoice_four_items_19_7_tax_with_skonto_4x_25_multiple() {
1019 reset_state() if $ALWAYS_RESET;
1021 my $item1 = new_item(qty => 0.5, part => $parts[2]);
1022 my $item2 = new_item(qty => 0.5, part => $parts[3]);
1023 my $item3 = new_item(qty => 0.5, part => $parts[2]);
1024 my $item4 = new_item(qty => 0.5, part => $parts[3]);
1025 my $invoice = new_invoice(
1027 invoiceitems => [ $item1, $item2, $item3, $item4 ],
1028 payment_id => $payment_terms->id,
1032 $invoice->pay_invoice( amount => '90',
1033 payment_type => 'without_skonto',
1034 chart_id => $bank_account->chart_id,
1035 transdate => DateTime->today_local->to_kivitendo
1037 $invoice->pay_invoice( payment_type => 'difference_as_skonto',
1038 chart_id => $bank_account->chart_id,
1039 transdate => DateTime->today_local->to_kivitendo
1042 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
1043 my $total = total_amount($invoice);
1045 my $title = 'default invoice, four items, 19/7% tax with_skonto_pt 4x25';
1047 is($invoice->netamount, 100, "${title}: netamount");
1048 is($invoice->amount, 113, "${title}: amount");
1049 is($paid_amount, -113, "${title}: paid amount");
1050 is($invoice->paid, 113, "${title}: paid");
1051 is($number_of_payments, 3, "${title}: 3 AR_paid bookings");
1052 is($total, 0, "${title}: even balance: this will fail due to rounding error in invoice post, not the skonto");
1055 sub test_ar_currency_tax_not_included_and_payment {
1056 my $netamount = $::form->round_amount(75 * $exchangerate->sell,2); # 75 in CUR, 100.00 in EUR
1057 my $amount = $::form->round_amount($netamount * 1.19,2); # 100 in CUR, 119.00 in EUR
1058 my $invoice = SL::DB::Invoice->new(
1061 netamount => $netamount,
1062 transdate => $transdate1,
1064 customer_id => $customer->id,
1065 taxzone_id => $customer->taxzone_id,
1066 currency_id => $currency->id,
1068 notes => 'test_ar_currency_tax_not_included_and_payment',
1070 $invoice->add_ar_amount_row(
1071 amount => $invoice->netamount,
1072 chart => $ar_amount_chart,
1076 $invoice->create_ar_row(chart => $ar_chart);
1079 is(SL::DB::Manager::Invoice->get_all_count(where => [ invoice => 0 ]), 1, 'there is one ar transaction');
1080 is($invoice->currency_id , $currency->id , 'currency_id has been saved');
1081 is($invoice->netamount , 100 , 'ar amount has been converted');
1082 is($invoice->amount , 119 , 'ar amount has been converted');
1083 is($invoice->taxincluded , 0 , 'ar transaction doesn\'t have taxincluded');
1084 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');
1085 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');
1087 $invoice->pay_invoice(chart_id => $bank->id,
1090 transdate => $transdate1->to_kivitendo,
1092 $invoice->pay_invoice(chart_id => $bank->id,
1095 transdate => $transdate1->to_kivitendo,
1097 # $invoice->pay_invoice(chart_id => $bank->id,
1099 # transdate => $transdate2->to_kivitendo,
1101 is(scalar @{$invoice->transactions}, 9, 'ar transaction has 9 transactions (incl. fxtransactions)');
1102 is($invoice->paid, $invoice->amount, 'ar transaction paid = amount in default currency');
1105 sub test_ar_currency_tax_included {
1106 # we want the acc_trans amount to be 100
1107 my $amount = $::form->round_amount(75 * $exchangerate->sell * 1.19);
1108 my $netamount = $::form->round_amount($amount / 1.19,2);
1109 my $invoice = SL::DB::Invoice->new(
1113 transdate => $transdate1,
1115 customer_id => $customer->id,
1116 taxzone_id => $customer->taxzone_id,
1117 currency_id => $currency->id,
1118 notes => 'test_ar_currency_tax_included',
1121 $invoice->add_ar_amount_row( # should take care of taxincluded
1122 amount => $invoice->amount, # tax included in local currency
1123 chart => $ar_amount_chart,
1127 $invoice->create_ar_row( chart => $ar_chart );
1129 is(SL::DB::Manager::Invoice->get_all_count(where => [ invoice => 0 ]), 2, 'there are now two ar transactions');
1130 is($invoice->currency_id , $currency->id , 'currency_id has been saved');
1131 is($invoice->amount , $amount , 'amount ok');
1132 is($invoice->netamount , $netamount , 'netamount ok');
1133 is($invoice->taxincluded , 1 , 'ar transaction has taxincluded');
1134 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');
1135 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');
1136 $invoice->pay_invoice(chart_id => $bank->id,
1139 transdate => $transdate1->to_kivitendo,
1144 sub test_ap_currency_tax_not_included_and_payment {
1145 my $netamount = $::form->round_amount(75 * $exchangerate->buy,2); # 75 in CUR, 100.00 in EUR
1146 my $amount = $::form->round_amount($netamount * 1.19,2); # 100 in CUR, 119.00 in EUR
1147 my $invoice = SL::DB::PurchaseInvoice->new(
1149 invnumber => 'test_ap_currency_tax_not_included_and_payment',
1151 netamount => $netamount,
1152 transdate => $transdate1,
1154 vendor_id => $vendor->id,
1155 taxzone_id => $vendor->taxzone_id,
1156 currency_id => $currency->id,
1158 notes => 'test_ap_currency_tax_not_included_and_payment',
1160 $invoice->add_ap_amount_row(
1161 amount => $invoice->netamount,
1162 chart => $ap_amount_chart,
1163 tax_id => $tax_9->id,
1166 $invoice->create_ap_row(chart => $ap_chart);
1169 is($invoice->currency_id, $currency->id, 'currency_id has been saved');
1170 is($invoice->netamount, 100, 'ap amount has been converted');
1171 is($invoice->amount, 119, 'ap amount has been converted');
1172 is($invoice->taxincluded, 0, 'ap transaction doesn\'t have taxincluded');
1173 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');
1174 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');
1176 $invoice->pay_invoice(chart_id => $bank->id,
1179 transdate => $transdate1->to_kivitendo,
1181 $invoice->pay_invoice(chart_id => $bank->id,
1184 transdate => $transdate1->to_kivitendo,
1186 is(scalar @{$invoice->transactions}, 9, 'ap transaction has 9 transactions (incl. fxtransactions)');
1187 is($invoice->paid, $invoice->amount, 'ap transaction paid = amount in default currency');
1190 sub test_ap_currency_tax_included {
1191 # we want the acc_trans amount to be 100
1192 my $amount = $::form->round_amount(75 * $exchangerate->buy * 1.19);
1193 my $netamount = $::form->round_amount($amount / 1.19,2);
1194 my $invoice = SL::DB::PurchaseInvoice->new(
1196 amount => 119, #$amount,
1197 netamount => 100, #$netamount,
1198 transdate => $transdate1,
1200 vendor_id => $vendor->id,
1201 taxzone_id => $vendor->taxzone_id,
1202 currency_id => $currency->id,
1203 notes => 'test_ap_currency_tax_included',
1204 invnumber => 'test_ap_currency_tax_included',
1207 $invoice->add_ap_amount_row( # should take care of taxincluded
1208 amount => $invoice->amount, # tax included in local currency
1209 chart => $ap_amount_chart,
1210 tax_id => $tax_9->id,
1213 $invoice->create_ap_row( chart => $ap_chart );
1215 is($invoice->currency_id , $currency->id , 'currency_id has been saved');
1216 is($invoice->amount , $amount , 'amount ok');
1217 is($invoice->netamount , $netamount , 'netamount ok');
1218 is($invoice->taxincluded , 1 , 'ap transaction has taxincluded');
1219 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');
1220 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');
1222 $invoice->pay_invoice(chart_id => $bank->id,
1225 transdate => $transdate1->to_kivitendo,
1230 sub test_ar_currency_tax_not_included_and_payment_2 {
1231 my $title = 'test_ar_currency_tax_not_included_and_payment_2';
1232 my $netamount = $::form->round_amount(125 * $exchangerate2->sell,2); # 125.00 in CUR, 100.00 in EUR
1233 my $amount = $::form->round_amount($netamount * 1.19,2); # 148.75 in CUR, 119.00 in EUR
1234 my $invoice = SL::DB::Invoice->new(
1237 netamount => $netamount,
1238 transdate => $transdate2,
1240 customer_id => $customer->id,
1241 taxzone_id => $customer->taxzone_id,
1242 currency_id => $currency->id,
1244 notes => 'test_ar_currency_tax_not_included_and_payment 0.8',
1245 invnumber => 'test_ar_currency_tax_not_included_and_payment 0.8',
1247 $invoice->add_ar_amount_row(
1248 amount => $invoice->netamount,
1249 chart => $ar_amount_chart,
1253 $invoice->create_ar_row(chart => $ar_chart);
1256 is($invoice->currency_id , $currency->id , "$title: currency_id has been saved");
1257 is($invoice->netamount , 100 , "$title: ar amount has been converted");
1258 is($invoice->amount , 119 , "$title: ar amount has been converted");
1259 is($invoice->taxincluded , 0 , "$title: ar transaction doesn\"t have taxincluded");
1260 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");
1261 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');
1263 $invoice->pay_invoice(chart_id => $bank->id,
1266 transdate => $transdate2->to_kivitendo,
1268 $invoice->pay_invoice(chart_id => $bank->id,
1271 transdate => $transdate3->to_kivitendo,
1273 $invoice->pay_invoice(chart_id => $bank->id,
1276 transdate => $transdate4->to_kivitendo,
1278 # $invoice->pay_invoice(chart_id => $bank->id,
1280 # transdate => $transdate2->to_kivitendo,
1282 my $fx_transactions = SL::DB::Manager::AccTransaction->get_all(where => [ trans_id => $invoice->id, fx_transaction => 1 ], sort_by => ('acc_trans_id'));
1283 is(scalar @{$fx_transactions}, 3, "$title: ar transaction has 3 fx transactions");
1284 is($fx_transactions->[0]->amount, '24.69000', "$title fx transactions 1: 123.45-(123.45*0.8) = 24.69");
1286 is(scalar @{$invoice->transactions}, 14, "$title ar transaction has 14 transactions (incl. fxtransactions and fx_gain)");
1287 is($invoice->paid, $invoice->amount, "$title ar transaction paid = amount in default currency");
1290 sub test_ar_currency_tax_not_included_and_payment_2_credit_note {
1291 my $netamount = $::form->round_amount(-125 * $exchangerate2->sell,2); # 125.00 in CUR, 100.00 in EUR
1292 my $amount = $::form->round_amount($netamount * 1.19,2); # 148.75 in CUR, 119.00 in EUR
1293 my $invoice = SL::DB::Invoice->new(
1296 netamount => $netamount,
1297 transdate => $transdate2,
1299 customer_id => $customer->id,
1300 taxzone_id => $customer->taxzone_id,
1301 currency_id => $currency->id,
1303 notes => 'test_ar_currency_tax_not_included_and_payment credit note 0.8',
1304 invnumber => 'test_ar_currency_tax_not_included_and_payment credit note 0.8',
1306 $invoice->add_ar_amount_row(
1307 amount => $invoice->netamount,
1308 chart => $ar_amount_chart,
1312 $invoice->create_ar_row(chart => $ar_chart);
1315 is($invoice->currency_id , $currency->id , 'currency_id has been saved');
1316 is($invoice->netamount , -100 , 'ar amount has been converted');
1317 is($invoice->amount , -119 , 'ar amount has been converted');
1318 is($invoice->taxincluded , 0 , 'ar transaction doesn\'t have taxincluded');
1319 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');
1320 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');
1322 $invoice->pay_invoice(chart_id => $bank->id,
1325 transdate => $transdate2->to_kivitendo,
1327 $invoice->pay_invoice(chart_id => $bank->id,
1330 transdate => $transdate2->to_kivitendo,
1332 my $fx_transactions = SL::DB::Manager::AccTransaction->get_all(where => [ trans_id => $invoice->id, fx_transaction => 1 ], sort_by => ('acc_trans_id'));
1333 is(scalar @{$fx_transactions}, 2, 'ar transaction has 2 fx transactions');
1334 is($fx_transactions->[0]->amount, '-24.69000', 'fx transactions 1: 123.45-(123.45*0.8) = 24.69');
1336 is(scalar @{$invoice->transactions}, 9, 'ar transaction has 9 transactions (incl. fxtransactions)');
1337 is($invoice->paid, $invoice->amount, 'ar transaction paid = amount in default currency');
1340 sub test_ap_currency_tax_not_included_and_payment_2 {
1341 my $title = 'test_ap_currency_tax_not_included_and_payment_2';
1342 my $netamount = $::form->round_amount(125 * $exchangerate2->sell,2); # 125.00 in CUR, 100.00 in EUR
1343 my $amount = $::form->round_amount($netamount * 1.19,2); # 148.75 in CUR, 119.00 in EUR
1344 my $invoice = SL::DB::PurchaseInvoice->new(
1347 netamount => $netamount,
1348 transdate => $transdate2,
1350 vendor_id => $vendor->id,
1351 taxzone_id => $vendor->taxzone_id,
1352 currency_id => $currency->id,
1354 notes => 'test_ap_currency_tax_not_included_and_payment_2 0.8 + 1.33333',
1355 invnumber => 'test_ap_currency_tax_not_included_and_payment_2 0.8 + 1.33333',
1357 $invoice->add_ap_amount_row(
1358 amount => $invoice->netamount,
1359 chart => $ap_amount_chart,
1360 tax_id => $tax_9->id,
1363 $invoice->create_ap_row(chart => $ap_chart);
1366 is($invoice->currency_id , $currency->id , "$title: currency_id has been saved");
1367 is($invoice->netamount , 100 , "$title: ap amount has been converted");
1368 is($invoice->amount , 119 , "$title: ap amount has been converted");
1369 is($invoice->taxincluded , 0 , "$title: ap transaction doesn\'t have taxincluded");
1370 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');
1371 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');
1373 $invoice->pay_invoice(chart_id => $bank->id,
1376 transdate => $transdate2->to_kivitendo,
1378 $invoice->pay_invoice(chart_id => $bank->id,
1381 transdate => $transdate3->to_kivitendo,
1383 $invoice->pay_invoice(chart_id => $bank->id,
1386 transdate => $transdate4->to_kivitendo,
1388 my $fx_transactions = SL::DB::Manager::AccTransaction->get_all(where => [ trans_id => $invoice->id, fx_transaction => 1 ], sort_by => ('acc_trans_id'));
1389 is(scalar @{$fx_transactions}, 3, "$title: ap transaction has 3 fx transactions");
1390 is($fx_transactions->[0]->amount, '-2.00000', "$title: fx transaction 1: 10.00-( 10.00*0.80000) = 2.00000");
1391 is($fx_transactions->[1]->amount, '68.59000', "$title: fx transaction 2: 123.45-(123.45*1.55557) = -68.58511");
1392 is($fx_transactions->[2]->amount, '-3.40000', "$title: fx transaction 3: 15.30-(15.30 *0.77777) = 3.40012");
1394 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'));
1395 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'));
1396 is($fx_gain_transactions->[0]->amount, '0.34000', "$title: fx gain amount ok");
1397 is($fx_loss_transactions->[0]->amount, '-93.28000', "$title: fx loss amount ok");
1399 is(scalar @{$invoice->transactions}, 14, "$title: ap transaction has 14 transactions (incl. fxtransactions and gain_loss)");
1400 is($invoice->paid, $invoice->amount, "$title: ap transaction paid = amount in default currency");
1401 is(total_amount($invoice), 0, "$title: even balance");
1404 sub test_ap_currency_tax_not_included_and_payment_2_credit_note {
1405 my $title = 'test_ap_currency_tax_not_included_and_payment_2_credit_note';
1406 my $netamount = $::form->round_amount(-125 * $exchangerate2->sell,2); # 125.00 in CUR, 100.00 in EUR
1407 my $amount = $::form->round_amount($netamount * 1.19,2); # 148.75 in CUR, 119.00 in EUR
1408 my $invoice = SL::DB::PurchaseInvoice->new(
1411 netamount => $netamount,
1412 transdate => $transdate2,
1414 vendor_id => $vendor->id,
1415 taxzone_id => $vendor->taxzone_id,
1416 currency_id => $currency->id,
1418 notes => 'test_ap_currency_tax_not_included_and_payment credit note 0.8 + 1.33333',
1419 invnumber => 'test_ap_currency_tax_not_included_and_payment credit note 0.8 + 1.33333',
1421 $invoice->add_ap_amount_row(
1422 amount => $invoice->netamount,
1423 chart => $ap_amount_chart,
1424 tax_id => $tax_9->id,
1427 $invoice->create_ap_row(chart => $ap_chart);
1430 is($invoice->currency_id , $currency->id , "$title: currency_id has been saved");
1431 is($invoice->netamount , -100 , "$title: ap amount has been converted");
1432 is($invoice->amount , -119 , "$title: ap amount has been converted");
1433 is($invoice->taxincluded , 0 , "$title: ap transaction doesn\'t have taxincluded");
1434 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');
1435 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');
1437 $invoice->pay_invoice(chart_id => $bank->id,
1440 transdate => $transdate2->to_kivitendo,
1442 $invoice->pay_invoice(chart_id => $bank->id,
1445 transdate => $transdate3->to_kivitendo,
1447 $invoice->pay_invoice(chart_id => $bank->id,
1450 transdate => $transdate4->to_kivitendo,
1452 my $fx_transactions = SL::DB::Manager::AccTransaction->get_all(where => [ trans_id => $invoice->id, fx_transaction => 1 ], sort_by => ('acc_trans_id'));
1453 is(scalar @{$fx_transactions}, 3, "$title: ap transaction has 3 fx transactions");
1454 is($fx_transactions->[0]->amount, '2.00000', "$title: fx transaction 1: 10.00-( 10.00*0.80000) = 2.00000");
1455 is($fx_transactions->[1]->amount, '-68.59000', "$title: fx transaction 2: 123.45-(123.45*1.55557) = -68.58511");
1456 is($fx_transactions->[2]->amount, '3.40000', "$title: fx transaction 3: 15.30-(15.30 *0.77777) = 3.40012");
1458 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'));
1459 is($fx_gain_loss_transactions->[0]->amount, '93.28000', "$title: fx gain loss amount ok");
1461 is(scalar @{$invoice->transactions}, 14, "$title: ap transaction has 14 transactions (incl. fxtransactions and gain_loss)");
1462 is($invoice->paid, $invoice->amount, "$title: ap transaction paid = amount in default currency");
1463 is(total_amount($invoice), 0, "$title: even balance");
1466 Support::TestSetup::login();
1468 # test cases: without_skonto
1469 test_default_invoice_one_item_19_without_skonto();
1470 test_default_invoice_two_items_19_7_tax_with_skonto();
1471 test_default_invoice_two_items_19_7_without_skonto();
1472 test_default_invoice_two_items_19_7_without_skonto_incomplete_payment();
1473 test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments();
1474 test_default_purchase_invoice_two_charts_19_7_without_skonto();
1475 test_default_purchase_invoice_two_charts_19_7_tax_partial_unrounded_payment_without_skonto();
1476 test_default_invoice_one_item_19_without_skonto_overpaid();
1478 # test cases: difference_as_skonto
1479 test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto();
1480 test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto_1cent();
1481 test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto_2cent();
1482 test_default_invoice_one_item_19_multiple_payment_final_difference_as_skonto();
1483 test_default_invoice_one_item_19_multiple_payment_final_difference_as_skonto_1cent();
1484 test_default_purchase_invoice_two_charts_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto();
1486 # test cases: with_skonto_pt
1487 test_default_invoice_two_items_19_7_tax_with_skonto_50_50();
1488 test_default_invoice_four_items_19_7_tax_with_skonto_4x_25();
1489 test_default_invoice_four_items_19_7_tax_with_skonto_4x_25_multiple();
1490 test_default_purchase_invoice_two_charts_19_7_with_skonto();
1491 test_default_invoice_four_items_19_7_tax_with_skonto_4x_25_tax_included();
1492 test_default_invoice_two_items_19_7_tax_with_skonto_tax_included();
1494 # test payment of ar and ap transactions with currency and tax included/not included
1495 # exchangerate = 1.33333
1496 test_ar_currency_tax_not_included_and_payment();
1497 test_ar_currency_tax_included();
1498 test_ap_currency_tax_not_included_and_payment();
1499 test_ap_currency_tax_included();
1501 test_ar_currency_tax_not_included_and_payment_2(); # exchangerate 0.8
1502 test_ar_currency_tax_not_included_and_payment_2_credit_note(); # exchangerate 0.8
1504 test_ap_currency_tax_not_included_and_payment_2(); # two exchangerates, with fx_gain_loss
1505 test_ap_currency_tax_not_included_and_payment_2_credit_note(); # two exchangerates, with fx_gain_loss
1507 { local $TODO = "currently this test fails because the code writing the invoice is buggy, the calculation of skonto is correct";
1508 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");
1511 # remove all created data at end of test