9 use Support::TestSetup;
11 use List::Util qw(sum);
14 use SL::Dev::CustomerVendor;
16 use SL::DB::Buchungsgruppe;
18 use SL::DB::Exchangerate;
26 use SL::DB::BankAccount;
27 use SL::DB::PaymentTerm;
28 use SL::DBUtils qw(selectfirst_array_query);
31 my ($customer, $vendor, $currency_id, @parts, $buchungsgruppe, $buchungsgruppe7, $unit, $employee, $tax, $tax7, $tax_9, $taxzone, $payment_terms, $bank_account);
32 my ($transdate1, $transdate2, $transdate3, $transdate4, $currency, $exchangerate, $exchangerate2, $exchangerate3, $exchangerate4);
33 my ($ar_chart,$bank,$ar_amount_chart, $ap_chart, $ap_amount_chart, $fxloss_chart, $fxgain_chart);
37 my $reset_state_counter = 0;
39 my $purchase_invoice_counter = 0; # used for generating purchase invnumber
42 SL::DB::Manager::InvoiceItem->delete_all(all => 1);
43 SL::DB::Manager::Invoice->delete_all(all => 1);
44 SL::DB::Manager::PurchaseInvoice->delete_all(all => 1);
45 SL::DB::Manager::Part->delete_all(all => 1);
46 SL::DB::Manager::Customer->delete_all(all => 1);
47 SL::DB::Manager::Vendor->delete_all(all => 1);
48 SL::DB::Manager::BankAccount->delete_all(all => 1);
49 SL::DB::Manager::PaymentTerm->delete_all(all => 1);
50 SL::DB::Manager::Exchangerate->delete_all(all => 1);
51 SL::DB::Manager::Currency->delete_all(where => [ name => 'CUR' ]);
57 return if $reset_state_counter;
59 $params{$_} ||= {} for qw(buchungsgruppe unit customer part tax vendor);
63 $transdate1 = DateTime->today;
64 $transdate2 = DateTime->today->add(days => 1);
65 $transdate3 = DateTime->today->add(days => 2);
66 $transdate4 = DateTime->today->add(days => 3);
68 $buchungsgruppe = SL::DB::Manager::Buchungsgruppe->find_by(description => 'Standard 19%', %{ $params{buchungsgruppe} }) || croak "No accounting group";
69 $buchungsgruppe7 = SL::DB::Manager::Buchungsgruppe->find_by(description => 'Standard 7%') || croak "No accounting group for 7\%";
70 $unit = SL::DB::Manager::Unit->find_by(name => 'kg', %{ $params{unit} }) || croak "No unit";
71 $employee = SL::DB::Manager::Employee->current || croak "No employee";
72 $tax = SL::DB::Manager::Tax->find_by(taxkey => 3, 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 $taxzone = SL::DB::Manager::TaxZone->find_by( description => 'Inland') || croak "No taxzone";
75 $tax_9 = SL::DB::Manager::Tax->find_by(taxkey => 9, rate => 0.19, %{ $params{tax} }) || croak "No tax";
76 # $tax7 = SL::DB::Manager::Tax->find_by(taxkey => 2, rate => 0.07) || croak "No tax for 7\%";
78 $currency_id = $::instance_conf->get_currency_id;
80 $currency = SL::DB::Currency->new(name => 'CUR')->save;
82 $fxgain_chart = SL::DB::Manager::Chart->find_by(accno => '2660') or die "Can't find fxgain_chart in test";
83 $fxloss_chart = SL::DB::Manager::Chart->find_by(accno => '2150') or die "Can't find fxloss_chart in test";
85 $currency->db->dbh->do('UPDATE defaults SET fxgain_accno_id = ' . $fxgain_chart->id);
86 $currency->db->dbh->do('UPDATE defaults SET fxloss_accno_id = ' . $fxloss_chart->id);
87 $::instance_conf->reload->data;
88 is($fxgain_chart->id, $::instance_conf->get_fxgain_accno_id, "fxgain_chart was updated in defaults");
89 is($fxloss_chart->id, $::instance_conf->get_fxloss_accno_id, "fxloss_chart was updated in defaults");
91 $exchangerate = SL::DB::Exchangerate->new(transdate => $transdate1,
94 currency_id => $currency->id,
96 $exchangerate2 = SL::DB::Exchangerate->new(transdate => $transdate2,
99 currency_id => $currency->id,
101 $exchangerate3 = SL::DB::Exchangerate->new(transdate => $transdate3,
104 currency_id => $currency->id,
106 $exchangerate4 = SL::DB::Exchangerate->new(transdate => $transdate4,
109 currency_id => $currency->id,
112 $customer = SL::Dev::CustomerVendor::create_customer(
113 name => 'Test Customer',
114 currency_id => $currency_id,
115 taxzone_id => $taxzone->id,
118 $bank_account = SL::DB::BankAccount->new(
119 account_number => '123',
124 chart_id => SL::DB::Manager::Chart->find_by( description => 'Bank' )->id,
125 name => SL::DB::Manager::Chart->find_by( description => 'Bank' )->description,
128 $payment_terms = SL::DB::PaymentTerm->new(
129 description => 'payment',
130 description_long => 'payment',
133 percent_skonto => '0.05',
134 auto_calculation => 1,
137 $vendor = SL::Dev::CustomerVendor::create_vendor(
138 name => 'Test Vendor',
139 currency_id => $currency_id,
140 taxzone_id => $taxzone->id,
141 payment_id => $payment_terms->id,
146 push @parts, SL::Dev::Part::create_part(
147 partnumber => 'T4254',
148 description => 'Fourty-two fifty-four',
151 buchungsgruppen_id => $buchungsgruppe->id,
156 push @parts, SL::Dev::Part::create_part(
157 partnumber => 'T0815',
158 description => 'Zero EIGHT fifteeN @ 7%',
161 buchungsgruppen_id => $buchungsgruppe7->id,
165 push @parts, SL::Dev::Part::create_part(
167 description => 'Testware 19%',
170 buchungsgruppen_id => $buchungsgruppe->id,
174 push @parts, SL::Dev::Part::create_part(
176 description => 'Testware 7%',
179 buchungsgruppen_id => $buchungsgruppe7->id,
184 $ar_chart = SL::DB::Manager::Chart->find_by( accno => '1400' ); # Forderungen
185 $ap_chart = SL::DB::Manager::Chart->find_by( accno => '1600' ); # Verbindlichkeiten
186 $bank = SL::DB::Manager::Chart->find_by( accno => '1200' ); # Bank
187 $ar_amount_chart = SL::DB::Manager::Chart->find_by( accno => '8400' ); # Erlöse
188 $ap_amount_chart = SL::DB::Manager::Chart->find_by( accno => '3400' ); # Wareneingang 19%
190 $reset_state_counter++;
193 sub new_purchase_invoice {
195 # manually create a Kreditorenbuchung from scratch, ap + acc_trans bookings, as no helper exists yet, like $invoice->post.
196 # arap-Booking must come last in the acc_trans order
197 $purchase_invoice_counter++;
199 my $purchase_invoice = SL::DB::PurchaseInvoice->new(
200 vendor_id => $vendor->id,
201 invnumber => 'newap ' . $purchase_invoice_counter ,
202 currency_id => $currency_id,
203 employee_id => $employee->id,
204 gldate => $transdate1,
205 taxzone_id => $taxzone->id,
206 transdate => $transdate1,
216 my $expense_chart = SL::DB::Manager::Chart->find_by(accno => '3400');
217 my $expense_chart_booking= SL::DB::AccTransaction->new(
218 trans_id => $purchase_invoice->id,
219 chart_id => $expense_chart->id,
220 chart_link => $expense_chart->link,
222 transdate => $transdate1,
225 tax_id => SL::DB::Manager::Tax->find_by(taxkey => 9)->id);
226 $expense_chart_booking->save;
228 my $tax_chart = SL::DB::Manager::Chart->find_by(accno => '1576');
229 my $tax_chart_booking= SL::DB::AccTransaction->new(
230 trans_id => $purchase_invoice->id,
231 chart_id => $tax_chart->id,
232 chart_link => $tax_chart->link,
234 transdate => $transdate1,
237 tax_id => SL::DB::Manager::Tax->find_by(taxkey => 9)->id);
238 $tax_chart_booking->save;
239 $expense_chart = SL::DB::Manager::Chart->find_by(accno => '3300');
240 $expense_chart_booking= SL::DB::AccTransaction->new(
241 trans_id => $purchase_invoice->id,
242 chart_id => $expense_chart->id,
243 chart_link => $expense_chart->link,
245 transdate => $transdate1,
248 tax_id => SL::DB::Manager::Tax->find_by(taxkey => 8)->id);
249 $expense_chart_booking->save;
252 $tax_chart = SL::DB::Manager::Chart->find_by(accno => '1571');
253 $tax_chart_booking= SL::DB::AccTransaction->new(
254 trans_id => $purchase_invoice->id,
255 chart_id => $tax_chart->id,
256 chart_link => $tax_chart->link,
258 transdate => $transdate1,
261 tax_id => SL::DB::Manager::Tax->find_by(taxkey => 8)->id);
262 $tax_chart_booking->save;
263 my $arap_chart = SL::DB::Manager::Chart->find_by(accno => '1600');
264 my $arap_booking= SL::DB::AccTransaction->new(trans_id => $purchase_invoice->id,
265 chart_id => $arap_chart->id,
266 chart_link => $arap_chart->link,
268 transdate => $transdate1,
271 tax_id => SL::DB::Manager::Tax->find_by(taxkey => 0)->id);
274 return $purchase_invoice;
277 sub number_of_payments {
280 my $number_of_payments;
282 foreach my $transaction ( @{ $invoice->transactions } ) {
283 if ( $transaction->chart_link =~ /(AR_paid|AP_paid)/ ) {
284 $paid_amount += $transaction->amount ;
285 $number_of_payments++;
288 return ($number_of_payments, $paid_amount);
294 my $total = sum map { $_->amount } @{ $invoice->transactions };
296 return $::form->round_amount($total, 5);
302 sub test_default_invoice_one_item_19_without_skonto() {
303 reset_state() if $ALWAYS_RESET;
305 my $item = SL::Dev::Record::create_invoice_item(part => $parts[0], qty => 2.5);
306 my $invoice = SL::Dev::Record::create_sales_invoice(
308 invoiceitems => [ $item ],
309 payment_id => $payment_terms->id,
312 my $purchase_invoice = new_purchase_invoice();
315 my %params = ( chart_id => $bank_account->chart_id,
316 transdate => DateTime->today_local->to_kivitendo
319 $params{amount} = '6.96';
320 $params{payment_type} = 'without_skonto';
322 $invoice->pay_invoice( %params );
324 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
325 my $total = total_amount($invoice);
327 my $title = 'default invoice, one item, 19% tax, without_skonto';
329 is($invoice->netamount, 5.85, "${title}: netamount");
330 is($invoice->amount, 6.96, "${title}: amount");
331 is($paid_amount, -6.96, "${title}: paid amount");
332 is($number_of_payments, 1, "${title}: 1 AR_paid booking");
333 is($invoice->paid, 6.96, "${title}: paid");
334 is($total, 0, "${title}: even balance");
338 sub test_default_invoice_one_item_19_without_skonto_overpaid() {
339 reset_state() if $ALWAYS_RESET;
341 my $item = SL::Dev::Record::create_invoice_item(part => $parts[0], qty => 2.5);
342 my $invoice = SL::Dev::Record::create_sales_invoice(
344 invoiceitems => [ $item ],
345 payment_id => $payment_terms->id,
348 my $purchase_invoice = new_purchase_invoice();
352 my %params = ( chart_id => $bank_account->chart_id,
353 transdate => DateTime->today_local->to_kivitendo
356 $params{amount} = '16.96';
357 $params{payment_type} = 'without_skonto';
358 $invoice->pay_invoice( %params );
360 $params{amount} = '-10.00';
361 $invoice->pay_invoice( %params );
363 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
364 my $total = total_amount($invoice);
366 my $title = 'default invoice, one item, 19% tax, without_skonto';
368 is($invoice->netamount, 5.85, "${title}: netamount");
369 is($invoice->amount, 6.96, "${title}: amount");
370 is($paid_amount, -6.96, "${title}: paid amount");
371 is($number_of_payments, 2, "${title}: 1 AR_paid booking");
372 is($invoice->paid, 6.96, "${title}: paid");
373 is($total, 0, "${title}: even balance");
379 sub test_default_invoice_two_items_19_7_tax_with_skonto() {
380 reset_state() if $ALWAYS_RESET;
382 my $item1 = SL::Dev::Record::create_invoice_item(part => $parts[0], qty => 2.5);
383 my $item2 = SL::Dev::Record::create_invoice_item(part => $parts[1], qty => 1.2);
384 my $invoice = SL::Dev::Record::create_sales_invoice(
386 invoiceitems => [ $item1, $item2 ],
387 payment_id => $payment_terms->id,
391 my %params = ( chart_id => $bank_account->chart_id,
392 transdate => DateTime->today_local->to_kivitendo
395 $params{payment_type} = 'with_skonto_pt';
396 $params{amount} = $invoice->amount_less_skonto;
398 $invoice->pay_invoice( %params );
400 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
401 my $total = total_amount($invoice);
403 my $title = 'default invoice, two items, 19/7% tax with_skonto_pt';
405 is($invoice->netamount, 5.85 + 11.66, "${title}: netamount");
406 is($invoice->amount, 6.96 + 12.48, "${title}: amount");
407 is($paid_amount, -19.44, "${title}: paid amount");
408 is($invoice->paid, 19.44, "${title}: paid");
409 is($number_of_payments, 3, "${title}: 3 AR_paid bookings");
410 is($total, 0, "${title}: even balance");
413 sub test_default_invoice_two_items_19_7_tax_with_skonto_tax_included() {
414 reset_state() if $ALWAYS_RESET;
416 my $item1 = SL::Dev::Record::create_invoice_item(part => $parts[0], qty => 2.5);
417 my $item2 = SL::Dev::Record::create_invoice_item(part => $parts[1], qty => 1.2);
418 my $invoice = SL::Dev::Record::create_sales_invoice(
420 invoiceitems => [ $item1, $item2 ],
421 payment_id => $payment_terms->id,
425 my %params = ( chart_id => $bank_account->chart_id,
426 transdate => DateTime->today_local->to_kivitendo
429 $params{payment_type} = 'with_skonto_pt';
430 $params{amount} = $invoice->amount_less_skonto;
432 $invoice->pay_invoice( %params );
434 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
435 my $total = total_amount($invoice);
437 my $title = 'default invoice, two items, 19/7% tax with_skonto_pt';
439 is($invoice->netamount, 15.82, "${title}: netamount");
440 is($invoice->amount, 17.51, "${title}: amount");
441 is($paid_amount, -17.51, "${title}: paid amount");
442 is($invoice->paid, 17.51, "${title}: paid");
443 is($number_of_payments, 3, "${title}: 3 AR_paid bookings");
444 { local $TODO = "currently this test fails because the code writing the invoice is buggy, the calculation of skonto is correct";
445 is($total, 0, "${title}: even balance");
449 # test 3 : two items, without skonto
450 sub test_default_invoice_two_items_19_7_without_skonto() {
451 reset_state() if $ALWAYS_RESET;
453 my $item1 = SL::Dev::Record::create_invoice_item(part => $parts[0], qty => 2.5);
454 my $item2 = SL::Dev::Record::create_invoice_item(part => $parts[1], qty => 1.2);
455 my $invoice = SL::Dev::Record::create_sales_invoice(
457 invoiceitems => [ $item1, $item2 ],
458 payment_id => $payment_terms->id,
462 my %params = ( chart_id => $bank_account->chart_id,
463 transdate => DateTime->today_local->to_kivitendo
466 $params{amount} = '19.44'; # pass full amount
467 $params{payment_type} = 'without_skonto';
469 $invoice->pay_invoice( %params );
471 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
472 my $total = total_amount($invoice);
474 my $title = 'default invoice, two items, 19/7% tax without skonto';
476 is($invoice->netamount, 5.85 + 11.66, "${title}: netamount");
477 is($invoice->amount, 6.96 + 12.48, "${title}: amount");
478 is($paid_amount, -19.44, "${title}: paid amount");
479 is($invoice->paid, 19.44, "${title}: paid");
480 is($number_of_payments, 1, "${title}: 1 AR_paid bookings");
481 is($total, 0, "${title}: even balance");
485 sub test_default_invoice_two_items_19_7_without_skonto_incomplete_payment() {
486 reset_state() if $ALWAYS_RESET;
488 my $item1 = SL::Dev::Record::create_invoice_item(part => $parts[0], qty => 2.5);
489 my $item2 = SL::Dev::Record::create_invoice_item(part => $parts[1], qty => 1.2);
490 my $invoice = SL::Dev::Record::create_sales_invoice(
492 invoiceitems => [ $item1, $item2 ],
493 payment_id => $payment_terms->id,
496 $invoice->pay_invoice( amount => '9.44',
497 payment_type => 'without_skonto',
498 chart_id => $bank_account->chart_id,
499 transdate => DateTime->today_local->to_kivitendo,
502 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
503 my $total = total_amount($invoice);
505 my $title = 'default invoice, two items, 19/7% tax without skonto incomplete payment';
507 is($invoice->netamount, 5.85 + 11.66, "${title}: netamount");
508 is($invoice->amount, 6.96 + 12.48, "${title}: amount");
509 is($paid_amount, -9.44, "${title}: paid amount");
510 is($invoice->paid, 9.44, "${title}: paid");
511 is($number_of_payments, 1, "${title}: 1 AR_paid bookings");
512 is($total, 0, "${title}: even balance");
516 sub test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments() {
517 reset_state() if $ALWAYS_RESET;
519 my $item1 = SL::Dev::Record::create_invoice_item(part => $parts[0], qty => 2.5);
520 my $item2 = SL::Dev::Record::create_invoice_item(part => $parts[1], qty => 1.2);
521 my $invoice = SL::Dev::Record::create_sales_invoice(
523 invoiceitems => [ $item1, $item2 ],
524 payment_id => $payment_terms->id,
527 $invoice->pay_invoice( amount => '9.44',
528 payment_type => 'without_skonto',
529 chart_id => $bank_account->chart_id,
530 transdate => DateTime->today_local->to_kivitendo
532 $invoice->pay_invoice( amount => '10.00',
533 chart_id => $bank_account->chart_id,
534 transdate => DateTime->today_local->to_kivitendo
537 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
538 my $total = total_amount($invoice);
540 my $title = 'default invoice, two items, 19/7% tax not included';
542 is($invoice->netamount, 5.85 + 11.66, "${title}: netamount");
543 is($invoice->amount, 6.96 + 12.48, "${title}: amount");
544 is($paid_amount, -19.44, "${title}: paid amount");
545 is($invoice->paid, 19.44, "${title}: paid");
546 is($number_of_payments, 2, "${title}: 2 AR_paid bookings");
547 is($total, 0, "${title}: even balance");
552 sub test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto() {
553 reset_state() if $ALWAYS_RESET;
555 my $item1 = SL::Dev::Record::create_invoice_item(part => $parts[0], qty => 2.5);
556 my $item2 = SL::Dev::Record::create_invoice_item(part => $parts[1], qty => 1.2);
557 my $invoice = SL::Dev::Record::create_sales_invoice(
559 invoiceitems => [ $item1, $item2 ],
560 payment_id => $payment_terms->id,
563 $invoice->pay_invoice( amount => '9.44',
564 payment_type => 'without_skonto',
565 chart_id => $bank_account->chart_id,
566 transdate => DateTime->today_local->to_kivitendo
568 $invoice->pay_invoice( amount => '8.73',
569 payment_type => 'without_skonto',
570 chart_id => $bank_account->chart_id,
571 transdate => DateTime->today_local->to_kivitendo
573 $invoice->pay_invoice( amount => $invoice->open_amount,
574 payment_type => 'difference_as_skonto',
575 chart_id => $bank_account->chart_id,
576 transdate => DateTime->today_local->to_kivitendo
579 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
580 my $total = total_amount($invoice);
582 my $title = 'default invoice, two items, 19/7% tax not included';
584 is($invoice->netamount, 5.85 + 11.66, "${title}: netamount");
585 is($invoice->amount, 6.96 + 12.48, "${title}: amount");
586 is($paid_amount, -19.44, "${title}: paid amount");
587 is($invoice->paid, 19.44, "${title}: paid");
588 is($number_of_payments, 4, "${title}: 4 AR_paid bookings");
589 is($total, 0, "${title}: even balance");
593 sub test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto_1cent() {
594 reset_state() if $ALWAYS_RESET;
596 # if there is only one cent left there can only be one skonto booking, the
597 # error handling should choose the highest amount, which is the 7% account
598 # (11.66) rather than the 19% account (5.85). The actual tax amount is
599 # higher for the 19% case, though (1.11 compared to 0.82)
601 my $item1 = SL::Dev::Record::create_invoice_item(part => $parts[0], qty => 2.5);
602 my $item2 = SL::Dev::Record::create_invoice_item(part => $parts[1], qty => 1.2);
603 my $invoice = SL::Dev::Record::create_sales_invoice(
605 invoiceitems => [ $item1, $item2 ],
606 payment_id => $payment_terms->id,
609 $invoice->pay_invoice( amount => '19.42',
610 payment_type => 'without_skonto',
611 chart_id => $bank_account->chart_id,
612 transdate => DateTime->today_local->to_kivitendo
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
620 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
621 my $total = total_amount($invoice);
623 my $title = 'default invoice, two items, 19/7% tax not included';
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, 3, "${title}: 2 AR_paid bookings");
630 is($total, 0, "${title}: even balance");
634 sub test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto_2cent() {
635 reset_state() if $ALWAYS_RESET;
637 # if there are two cents left there will be two skonto bookings, 1 cent each
638 my $item1 = SL::Dev::Record::create_invoice_item(part => $parts[0], qty => 2.5);
639 my $item2 = SL::Dev::Record::create_invoice_item(part => $parts[1], qty => 1.2);
640 my $invoice = SL::Dev::Record::create_sales_invoice(
642 invoiceitems => [ $item1, $item2 ],
643 payment_id => $payment_terms->id,
646 $invoice->pay_invoice( amount => '19.42',
647 payment_type => 'without_skonto',
648 chart_id => $bank_account->chart_id,
649 transdate => DateTime->today_local->to_kivitendo
651 $invoice->pay_invoice( amount => $invoice->open_amount,
652 payment_type => 'difference_as_skonto',
653 chart_id => $bank_account->chart_id,
654 transdate => DateTime->today_local->to_kivitendo
657 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
658 my $total = total_amount($invoice);
660 my $title = 'default invoice, two items, 19/7% tax not included';
662 is($invoice->netamount, 5.85 + 11.66, "${title}: netamount");
663 is($invoice->amount, 6.96 + 12.48, "${title}: amount");
664 is($paid_amount, -19.44, "${title}: paid amount");
665 is($invoice->paid, 19.44, "${title}: paid");
666 is($number_of_payments, 3, "${title}: 3 AR_paid bookings");
667 is($total, 0, "${title}: even balance");
671 sub test_default_invoice_one_item_19_multiple_payment_final_difference_as_skonto() {
672 reset_state() if $ALWAYS_RESET;
674 my $item = SL::Dev::Record::create_invoice_item(part => $parts[0], qty => 2.5);
675 my $invoice = SL::Dev::Record::create_sales_invoice(
677 invoiceitems => [ $item ],
678 payment_id => $payment_terms->id,
682 my %params = ( chart_id => $bank_account->chart_id,
683 transdate => DateTime->today_local->to_kivitendo
686 $params{amount} = '2.32';
687 $params{payment_type} = 'without_skonto';
688 $invoice->pay_invoice( %params );
690 $params{amount} = '3.81';
691 $params{payment_type} = 'without_skonto';
692 $invoice->pay_invoice( %params );
694 $params{amount} = $invoice->open_amount; # set amount, otherwise previous 3.81 is used
695 $params{payment_type} = 'difference_as_skonto';
696 $invoice->pay_invoice( %params );
698 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
699 my $total = total_amount($invoice);
701 my $title = 'default invoice, one item, 19% tax, without_skonto';
703 is($invoice->netamount, 5.85, "${title}: netamount");
704 is($invoice->amount, 6.96, "${title}: amount");
705 is($paid_amount, -6.96, "${title}: paid amount");
706 is($number_of_payments, 3, "${title}: 3 AR_paid booking");
707 is($invoice->paid, 6.96, "${title}: paid");
708 is($total, 0, "${title}: even balance");
712 sub test_default_invoice_one_item_19_multiple_payment_final_difference_as_skonto_1cent() {
713 reset_state() if $ALWAYS_RESET;
715 my $item = SL::Dev::Record::create_invoice_item(part => $parts[0], qty => 2.5);
716 my $invoice = SL::Dev::Record::create_sales_invoice(
718 invoiceitems => [ $item ],
719 payment_id => $payment_terms->id,
723 my %params = ( chart_id => $bank_account->chart_id,
724 transdate => DateTime->today_local->to_kivitendo
727 $params{amount} = '6.95';
728 $params{payment_type} = 'without_skonto';
729 $invoice->pay_invoice( %params );
731 $params{amount} = $invoice->open_amount; # set amount, otherwise previous value 6.95 is used
732 $params{payment_type} = 'difference_as_skonto';
733 $invoice->pay_invoice( %params );
735 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
736 my $total = total_amount($invoice);
738 my $title = 'default invoice, one item, 19% tax, without_skonto';
740 is($invoice->netamount, 5.85, "${title}: netamount");
741 is($invoice->amount, 6.96, "${title}: amount");
742 is($paid_amount, -6.96, "${title}: paid amount");
743 is($number_of_payments, 2, "${title}: 3 AR_paid booking");
744 is($invoice->paid, 6.96, "${title}: paid");
745 is($total, 0, "${title}: even balance");
749 # test 3 : two items, without skonto
750 sub test_default_purchase_invoice_two_charts_19_7_without_skonto() {
751 reset_state() if $ALWAYS_RESET;
753 my $purchase_invoice = new_purchase_invoice();
755 my %params = ( chart_id => $bank_account->chart_id,
756 transdate => DateTime->today_local->to_kivitendo
759 $params{amount} = '226'; # pass full amount
760 $params{payment_type} = 'without_skonto';
762 $purchase_invoice->pay_invoice( %params );
764 my ($number_of_payments, $paid_amount) = number_of_payments($purchase_invoice);
765 my $total = total_amount($purchase_invoice);
767 my $title = 'default invoice, two items, 19/7% tax without skonto';
769 is($paid_amount, 226, "${title}: paid amount");
770 is($number_of_payments, 1, "${title}: 1 AP_paid bookings");
771 is($total, 0, "${title}: even balance");
775 sub test_default_purchase_invoice_two_charts_19_7_with_skonto() {
776 reset_state() if $ALWAYS_RESET;
778 my $purchase_invoice = new_purchase_invoice();
780 my %params = ( chart_id => $bank_account->chart_id,
781 transdate => DateTime->today_local->to_kivitendo
784 # $params{amount} = '226'; # pass full amount
785 $params{payment_type} = 'with_skonto_pt';
787 $purchase_invoice->pay_invoice( %params );
789 my ($number_of_payments, $paid_amount) = number_of_payments($purchase_invoice);
790 my $total = total_amount($purchase_invoice);
792 my $title = 'default invoice, two items, 19/7% tax without skonto';
794 is($paid_amount, 226, "${title}: paid amount");
795 is($number_of_payments, 3, "${title}: 1 AP_paid bookings");
796 is($total, 0, "${title}: even balance");
800 sub test_default_purchase_invoice_two_charts_19_7_tax_partial_unrounded_payment_without_skonto() {
801 # check whether unrounded amounts passed via $params{amount} are rounded for without_skonto case
802 reset_state() if $ALWAYS_RESET;
803 my $purchase_invoice = new_purchase_invoice();
804 $purchase_invoice->pay_invoice(
805 amount => ( $purchase_invoice->amount / 3 * 2),
806 payment_type => 'without_skonto',
807 chart_id => $bank_account->chart_id,
808 transdate => DateTime->today_local->to_kivitendo
810 my ($number_of_payments, $paid_amount) = number_of_payments($purchase_invoice);
811 my $total = total_amount($purchase_invoice);
813 my $title = 'default purchase_invoice, two charts, 19/7% tax multiple payments with final difference as skonto';
815 is($paid_amount, 150.67, "${title}: paid amount");
816 is($number_of_payments, 1, "${title}: 1 AP_paid bookings");
817 is($total, 0, "${title}: even balance");
821 sub test_default_purchase_invoice_two_charts_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto() {
822 reset_state() if $ALWAYS_RESET;
824 my $purchase_invoice = new_purchase_invoice();
826 # pay 2/3 and 1/5, leaves 3.83% to be used as Skonto
827 $purchase_invoice->pay_invoice(
828 amount => ( $purchase_invoice->amount / 3 * 2),
829 payment_type => 'without_skonto',
830 chart_id => $bank_account->chart_id,
831 transdate => DateTime->today_local->to_kivitendo
833 $purchase_invoice->pay_invoice(
834 amount => ( $purchase_invoice->amount / 5 ),
835 payment_type => 'without_skonto',
836 chart_id => $bank_account->chart_id,
837 transdate => DateTime->today_local->to_kivitendo
839 $purchase_invoice->pay_invoice(
840 payment_type => 'difference_as_skonto',
841 chart_id => $bank_account->chart_id,
842 transdate => DateTime->today_local->to_kivitendo
845 my ($number_of_payments, $paid_amount) = number_of_payments($purchase_invoice);
846 my $total = total_amount($purchase_invoice);
848 my $title = 'default purchase_invoice, two charts, 19/7% tax multiple payments with final difference as skonto';
850 is($paid_amount, 226, "${title}: paid amount");
851 is($number_of_payments, 4, "${title}: 1 AP_paid bookings");
852 is($total, 0, "${title}: even balance");
857 sub test_default_invoice_two_items_19_7_tax_with_skonto_50_50() {
858 reset_state() if $ALWAYS_RESET;
860 my $item1 = SL::Dev::Record::create_invoice_item(part => $parts[2], qty => 1);
861 my $item2 = SL::Dev::Record::create_invoice_item(part => $parts[3], qty => 1);
862 my $invoice = SL::Dev::Record::create_sales_invoice(
864 invoiceitems => [ $item1, $item2 ],
865 payment_id => $payment_terms->id,
869 my %params = ( chart_id => $bank_account->chart_id,
870 transdate => DateTime->today_local->to_kivitendo
873 $params{amount} = $invoice->amount_less_skonto;
874 $params{payment_type} = 'with_skonto_pt';
876 $invoice->pay_invoice( %params );
878 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
879 my $total = total_amount($invoice);
881 my $title = 'default invoice, two items, 19/7% tax with_skonto_pt 50/50';
883 is($invoice->netamount, 100, "${title}: netamount");
884 is($invoice->amount, 113, "${title}: amount");
885 is($paid_amount, -113, "${title}: paid amount");
886 is($invoice->paid, 113, "${title}: paid");
887 is($number_of_payments, 3, "${title}: 3 AR_paid bookings");
888 is($total, 0, "${title}: even balance");
892 sub test_default_invoice_four_items_19_7_tax_with_skonto_4x_25() {
893 reset_state() if $ALWAYS_RESET;
895 my $item1 = SL::Dev::Record::create_invoice_item(part => $parts[2], qty => 0.5);
896 my $item2 = SL::Dev::Record::create_invoice_item(part => $parts[3], qty => 0.5);
897 my $item3 = SL::Dev::Record::create_invoice_item(part => $parts[2], qty => 0.5);
898 my $item4 = SL::Dev::Record::create_invoice_item(part => $parts[3], qty => 0.5);
899 my $invoice = SL::Dev::Record::create_sales_invoice(
901 invoiceitems => [ $item1, $item2, $item3, $item4 ],
902 payment_id => $payment_terms->id,
906 my %params = ( chart_id => $bank_account->chart_id,
907 transdate => DateTime->today_local->to_kivitendo
910 $params{amount} = $invoice->amount_less_skonto;
911 $params{payment_type} = 'with_skonto_pt';
913 $invoice->pay_invoice( %params );
915 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
916 my $total = total_amount($invoice);
918 my $title = 'default invoice, four items, 19/7% tax with_skonto_pt 4x25';
920 is($invoice->netamount , 100 , "${title}: netamount");
921 is($invoice->amount , 113 , "${title}: amount");
922 is($paid_amount , -113 , "${title}: paid amount");
923 is($invoice->paid , 113 , "${title}: paid");
924 is($number_of_payments , 3 , "${title}: 3 AR_paid bookings");
925 is($total , 0 , "${title}: even balance");
928 sub test_default_invoice_four_items_19_7_tax_with_skonto_4x_25_tax_included() {
929 reset_state() if $ALWAYS_RESET;
931 my $item1 = SL::Dev::Record::create_invoice_item(part => $parts[2], qty => 0.5);
932 my $item2 = SL::Dev::Record::create_invoice_item(part => $parts[3], qty => 0.5);
933 my $item3 = SL::Dev::Record::create_invoice_item(part => $parts[2], qty => 0.5);
934 my $item4 = SL::Dev::Record::create_invoice_item(part => $parts[3], qty => 0.5);
935 my $invoice = SL::Dev::Record::create_sales_invoice(
937 invoiceitems => [ $item1, $item2, $item3, $item4 ],
938 payment_id => $payment_terms->id,
942 my %params = ( chart_id => $bank_account->chart_id,
943 transdate => DateTime->today_local->to_kivitendo
946 $params{amount} = $invoice->amount_less_skonto;
947 $params{payment_type} = 'with_skonto_pt';
949 $invoice->pay_invoice( %params );
951 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
952 my $total = total_amount($invoice);
954 my $title = 'default invoice, four items, 19/7% tax with_skonto_pt 4x25';
956 is($invoice->netamount, 88.75, "${title}: netamount");
957 is($invoice->amount, 100, "${title}: amount");
958 is($paid_amount, -100, "${title}: paid amount");
959 is($invoice->paid, 100, "${title}: paid");
960 is($number_of_payments, 3, "${title}: 3 AR_paid bookings");
961 { local $TODO = "currently this test fails because the code writing the invoice is buggy, the calculation of skonto is correct";
962 is($total, 0, "${title}: even balance");
966 sub test_default_invoice_four_items_19_7_tax_with_skonto_4x_25_multiple() {
967 reset_state() if $ALWAYS_RESET;
969 my $item1 = SL::Dev::Record::create_invoice_item(part => $parts[2], qty => 0.5);
970 my $item2 = SL::Dev::Record::create_invoice_item(part => $parts[3], qty => 0.5);
971 my $item3 = SL::Dev::Record::create_invoice_item(part => $parts[2], qty => 0.5);
972 my $item4 = SL::Dev::Record::create_invoice_item(part => $parts[3], qty => 0.5);
973 my $invoice = SL::Dev::Record::create_sales_invoice(
975 invoiceitems => [ $item1, $item2, $item3, $item4 ],
976 payment_id => $payment_terms->id,
979 $invoice->pay_invoice( amount => '90',
980 payment_type => 'without_skonto',
981 chart_id => $bank_account->chart_id,
982 transdate => DateTime->today_local->to_kivitendo
984 $invoice->pay_invoice( payment_type => 'difference_as_skonto',
985 chart_id => $bank_account->chart_id,
986 transdate => DateTime->today_local->to_kivitendo
989 my ($number_of_payments, $paid_amount) = number_of_payments($invoice);
990 my $total = total_amount($invoice);
992 my $title = 'default invoice, four items, 19/7% tax with_skonto_pt 4x25';
994 is($invoice->netamount, 100, "${title}: netamount");
995 is($invoice->amount, 113, "${title}: amount");
996 is($paid_amount, -113, "${title}: paid amount");
997 is($invoice->paid, 113, "${title}: paid");
998 is($number_of_payments, 3, "${title}: 3 AR_paid bookings");
999 is($total, 0, "${title}: even balance: this will fail due to rounding error in invoice post, not the skonto");
1002 sub test_ar_currency_tax_not_included_and_payment {
1003 my $netamount = $::form->round_amount(75 * $exchangerate->sell,2); # 75 in CUR, 100.00 in EUR
1004 my $amount = $::form->round_amount($netamount * 1.19,2); # 100 in CUR, 119.00 in EUR
1005 my $invoice = SL::DB::Invoice->new(
1008 netamount => $netamount,
1009 transdate => $transdate1,
1011 customer_id => $customer->id,
1012 taxzone_id => $customer->taxzone_id,
1013 currency_id => $currency->id,
1015 notes => 'test_ar_currency_tax_not_included_and_payment',
1017 $invoice->add_ar_amount_row(
1018 amount => $invoice->netamount,
1019 chart => $ar_amount_chart,
1023 $invoice->create_ar_row(chart => $ar_chart);
1026 is(SL::DB::Manager::Invoice->get_all_count(where => [ invoice => 0 ]), 1, 'there is one ar transaction');
1027 is($invoice->currency_id , $currency->id , 'currency_id has been saved');
1028 is($invoice->netamount , 100 , 'ar amount has been converted');
1029 is($invoice->amount , 119 , 'ar amount has been converted');
1030 is($invoice->taxincluded , 0 , 'ar transaction doesn\'t have taxincluded');
1031 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');
1032 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');
1034 $invoice->pay_invoice(chart_id => $bank->id,
1037 transdate => $transdate1->to_kivitendo,
1039 $invoice->pay_invoice(chart_id => $bank->id,
1042 transdate => $transdate1->to_kivitendo,
1044 # $invoice->pay_invoice(chart_id => $bank->id,
1046 # transdate => $transdate2->to_kivitendo,
1048 is(scalar @{$invoice->transactions}, 9, 'ar transaction has 9 transactions (incl. fxtransactions)');
1049 is($invoice->paid, $invoice->amount, 'ar transaction paid = amount in default currency');
1052 sub test_ar_currency_tax_included {
1053 # we want the acc_trans amount to be 100
1054 my $amount = $::form->round_amount(75 * $exchangerate->sell * 1.19);
1055 my $netamount = $::form->round_amount($amount / 1.19,2);
1056 my $invoice = SL::DB::Invoice->new(
1060 transdate => $transdate1,
1062 customer_id => $customer->id,
1063 taxzone_id => $customer->taxzone_id,
1064 currency_id => $currency->id,
1065 notes => 'test_ar_currency_tax_included',
1068 $invoice->add_ar_amount_row( # should take care of taxincluded
1069 amount => $invoice->amount, # tax included in local currency
1070 chart => $ar_amount_chart,
1074 $invoice->create_ar_row( chart => $ar_chart );
1076 is(SL::DB::Manager::Invoice->get_all_count(where => [ invoice => 0 ]), 2, 'there are now two ar transactions');
1077 is($invoice->currency_id , $currency->id , 'currency_id has been saved');
1078 is($invoice->amount , $amount , 'amount ok');
1079 is($invoice->netamount , $netamount , 'netamount ok');
1080 is($invoice->taxincluded , 1 , 'ar transaction has taxincluded');
1081 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');
1082 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');
1083 $invoice->pay_invoice(chart_id => $bank->id,
1086 transdate => $transdate1->to_kivitendo,
1091 sub test_ap_currency_tax_not_included_and_payment {
1092 my $netamount = $::form->round_amount(75 * $exchangerate->buy,2); # 75 in CUR, 100.00 in EUR
1093 my $amount = $::form->round_amount($netamount * 1.19,2); # 100 in CUR, 119.00 in EUR
1094 my $invoice = SL::DB::PurchaseInvoice->new(
1096 invnumber => 'test_ap_currency_tax_not_included_and_payment',
1098 netamount => $netamount,
1099 transdate => $transdate1,
1101 vendor_id => $vendor->id,
1102 taxzone_id => $vendor->taxzone_id,
1103 currency_id => $currency->id,
1105 notes => 'test_ap_currency_tax_not_included_and_payment',
1107 $invoice->add_ap_amount_row(
1108 amount => $invoice->netamount,
1109 chart => $ap_amount_chart,
1110 tax_id => $tax_9->id,
1113 $invoice->create_ap_row(chart => $ap_chart);
1116 is($invoice->currency_id, $currency->id, 'currency_id has been saved');
1117 is($invoice->netamount, 100, 'ap amount has been converted');
1118 is($invoice->amount, 119, 'ap amount has been converted');
1119 is($invoice->taxincluded, 0, 'ap transaction doesn\'t have taxincluded');
1120 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');
1121 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');
1123 $invoice->pay_invoice(chart_id => $bank->id,
1126 transdate => $transdate1->to_kivitendo,
1128 $invoice->pay_invoice(chart_id => $bank->id,
1131 transdate => $transdate1->to_kivitendo,
1133 is(scalar @{$invoice->transactions}, 9, 'ap transaction has 9 transactions (incl. fxtransactions)');
1134 is($invoice->paid, $invoice->amount, 'ap transaction paid = amount in default currency');
1137 sub test_ap_currency_tax_included {
1138 # we want the acc_trans amount to be 100
1139 my $amount = $::form->round_amount(75 * $exchangerate->buy * 1.19);
1140 my $netamount = $::form->round_amount($amount / 1.19,2);
1141 my $invoice = SL::DB::PurchaseInvoice->new(
1143 amount => 119, #$amount,
1144 netamount => 100, #$netamount,
1145 transdate => $transdate1,
1147 vendor_id => $vendor->id,
1148 taxzone_id => $vendor->taxzone_id,
1149 currency_id => $currency->id,
1150 notes => 'test_ap_currency_tax_included',
1151 invnumber => 'test_ap_currency_tax_included',
1154 $invoice->add_ap_amount_row( # should take care of taxincluded
1155 amount => $invoice->amount, # tax included in local currency
1156 chart => $ap_amount_chart,
1157 tax_id => $tax_9->id,
1160 $invoice->create_ap_row( chart => $ap_chart );
1162 is($invoice->currency_id , $currency->id , 'currency_id has been saved');
1163 is($invoice->amount , $amount , 'amount ok');
1164 is($invoice->netamount , $netamount , 'netamount ok');
1165 is($invoice->taxincluded , 1 , 'ap transaction has taxincluded');
1166 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');
1167 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');
1169 $invoice->pay_invoice(chart_id => $bank->id,
1172 transdate => $transdate1->to_kivitendo,
1177 sub test_ar_currency_tax_not_included_and_payment_2 {
1178 my $title = 'test_ar_currency_tax_not_included_and_payment_2';
1179 my $netamount = $::form->round_amount(125 * $exchangerate2->sell,2); # 125.00 in CUR, 100.00 in EUR
1180 my $amount = $::form->round_amount($netamount * 1.19,2); # 148.75 in CUR, 119.00 in EUR
1181 my $invoice = SL::DB::Invoice->new(
1184 netamount => $netamount,
1185 transdate => $transdate2,
1187 customer_id => $customer->id,
1188 taxzone_id => $customer->taxzone_id,
1189 currency_id => $currency->id,
1191 notes => 'test_ar_currency_tax_not_included_and_payment 0.8',
1192 invnumber => 'test_ar_currency_tax_not_included_and_payment 0.8',
1194 $invoice->add_ar_amount_row(
1195 amount => $invoice->netamount,
1196 chart => $ar_amount_chart,
1200 $invoice->create_ar_row(chart => $ar_chart);
1203 is($invoice->currency_id , $currency->id , "$title: currency_id has been saved");
1204 is($invoice->netamount , 100 , "$title: ar amount has been converted");
1205 is($invoice->amount , 119 , "$title: ar amount has been converted");
1206 is($invoice->taxincluded , 0 , "$title: ar transaction doesn\"t have taxincluded");
1207 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");
1208 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');
1210 $invoice->pay_invoice(chart_id => $bank->id,
1213 transdate => $transdate2->to_kivitendo,
1215 $invoice->pay_invoice(chart_id => $bank->id,
1218 transdate => $transdate3->to_kivitendo,
1220 $invoice->pay_invoice(chart_id => $bank->id,
1223 transdate => $transdate4->to_kivitendo,
1225 # $invoice->pay_invoice(chart_id => $bank->id,
1227 # transdate => $transdate2->to_kivitendo,
1229 my $fx_transactions = SL::DB::Manager::AccTransaction->get_all(where => [ trans_id => $invoice->id, fx_transaction => 1 ], sort_by => ('acc_trans_id'));
1230 is(scalar @{$fx_transactions}, 3, "$title: ar transaction has 3 fx transactions");
1231 is($fx_transactions->[0]->amount, '24.69000', "$title fx transactions 1: 123.45-(123.45*0.8) = 24.69");
1233 is(scalar @{$invoice->transactions}, 14, "$title ar transaction has 14 transactions (incl. fxtransactions and fx_gain)");
1234 is($invoice->paid, $invoice->amount, "$title ar transaction paid = amount in default currency");
1237 sub test_ar_currency_tax_not_included_and_payment_2_credit_note {
1238 my $netamount = $::form->round_amount(-125 * $exchangerate2->sell,2); # 125.00 in CUR, 100.00 in EUR
1239 my $amount = $::form->round_amount($netamount * 1.19,2); # 148.75 in CUR, 119.00 in EUR
1240 my $invoice = SL::DB::Invoice->new(
1243 netamount => $netamount,
1244 transdate => $transdate2,
1246 customer_id => $customer->id,
1247 taxzone_id => $customer->taxzone_id,
1248 currency_id => $currency->id,
1250 notes => 'test_ar_currency_tax_not_included_and_payment credit note 0.8',
1251 invnumber => 'test_ar_currency_tax_not_included_and_payment credit note 0.8',
1253 $invoice->add_ar_amount_row(
1254 amount => $invoice->netamount,
1255 chart => $ar_amount_chart,
1259 $invoice->create_ar_row(chart => $ar_chart);
1262 is($invoice->currency_id , $currency->id , 'currency_id has been saved');
1263 is($invoice->netamount , -100 , 'ar amount has been converted');
1264 is($invoice->amount , -119 , 'ar amount has been converted');
1265 is($invoice->taxincluded , 0 , 'ar transaction doesn\'t have taxincluded');
1266 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');
1267 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');
1269 $invoice->pay_invoice(chart_id => $bank->id,
1272 transdate => $transdate2->to_kivitendo,
1274 $invoice->pay_invoice(chart_id => $bank->id,
1277 transdate => $transdate2->to_kivitendo,
1279 my $fx_transactions = SL::DB::Manager::AccTransaction->get_all(where => [ trans_id => $invoice->id, fx_transaction => 1 ], sort_by => ('acc_trans_id'));
1280 is(scalar @{$fx_transactions}, 2, 'ar transaction has 2 fx transactions');
1281 is($fx_transactions->[0]->amount, '-24.69000', 'fx transactions 1: 123.45-(123.45*0.8) = 24.69');
1283 is(scalar @{$invoice->transactions}, 9, 'ar transaction has 9 transactions (incl. fxtransactions)');
1284 is($invoice->paid, $invoice->amount, 'ar transaction paid = amount in default currency');
1287 sub test_ap_currency_tax_not_included_and_payment_2 {
1288 my $title = 'test_ap_currency_tax_not_included_and_payment_2';
1289 my $netamount = $::form->round_amount(125 * $exchangerate2->sell,2); # 125.00 in CUR, 100.00 in EUR
1290 my $amount = $::form->round_amount($netamount * 1.19,2); # 148.75 in CUR, 119.00 in EUR
1291 my $invoice = SL::DB::PurchaseInvoice->new(
1294 netamount => $netamount,
1295 transdate => $transdate2,
1297 vendor_id => $vendor->id,
1298 taxzone_id => $vendor->taxzone_id,
1299 currency_id => $currency->id,
1301 notes => 'test_ap_currency_tax_not_included_and_payment_2 0.8 + 1.33333',
1302 invnumber => 'test_ap_currency_tax_not_included_and_payment_2 0.8 + 1.33333',
1304 $invoice->add_ap_amount_row(
1305 amount => $invoice->netamount,
1306 chart => $ap_amount_chart,
1307 tax_id => $tax_9->id,
1310 $invoice->create_ap_row(chart => $ap_chart);
1313 is($invoice->currency_id , $currency->id , "$title: currency_id has been saved");
1314 is($invoice->netamount , 100 , "$title: ap amount has been converted");
1315 is($invoice->amount , 119 , "$title: ap amount has been converted");
1316 is($invoice->taxincluded , 0 , "$title: ap transaction doesn\'t have taxincluded");
1317 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');
1318 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');
1320 $invoice->pay_invoice(chart_id => $bank->id,
1323 transdate => $transdate2->to_kivitendo,
1325 $invoice->pay_invoice(chart_id => $bank->id,
1328 transdate => $transdate3->to_kivitendo,
1330 $invoice->pay_invoice(chart_id => $bank->id,
1333 transdate => $transdate4->to_kivitendo,
1335 my $fx_transactions = SL::DB::Manager::AccTransaction->get_all(where => [ trans_id => $invoice->id, fx_transaction => 1 ], sort_by => ('acc_trans_id'));
1336 is(scalar @{$fx_transactions}, 3, "$title: ap transaction has 3 fx transactions");
1337 is($fx_transactions->[0]->amount, '-2.00000', "$title: fx transaction 1: 10.00-( 10.00*0.80000) = 2.00000");
1338 is($fx_transactions->[1]->amount, '68.59000', "$title: fx transaction 2: 123.45-(123.45*1.55557) = -68.58511");
1339 is($fx_transactions->[2]->amount, '-3.40000', "$title: fx transaction 3: 15.30-(15.30 *0.77777) = 3.40012");
1341 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'));
1342 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'));
1343 is($fx_gain_transactions->[0]->amount, '0.34000', "$title: fx gain amount ok");
1344 is($fx_loss_transactions->[0]->amount, '-93.28000', "$title: fx loss amount ok");
1346 is(scalar @{$invoice->transactions}, 14, "$title: ap transaction has 14 transactions (incl. fxtransactions and gain_loss)");
1347 is($invoice->paid, $invoice->amount, "$title: ap transaction paid = amount in default currency");
1348 is(total_amount($invoice), 0, "$title: even balance");
1351 sub test_ap_currency_tax_not_included_and_payment_2_credit_note {
1352 my $title = 'test_ap_currency_tax_not_included_and_payment_2_credit_note';
1353 my $netamount = $::form->round_amount(-125 * $exchangerate2->sell,2); # 125.00 in CUR, 100.00 in EUR
1354 my $amount = $::form->round_amount($netamount * 1.19,2); # 148.75 in CUR, 119.00 in EUR
1355 my $invoice = SL::DB::PurchaseInvoice->new(
1358 netamount => $netamount,
1359 transdate => $transdate2,
1361 vendor_id => $vendor->id,
1362 taxzone_id => $vendor->taxzone_id,
1363 currency_id => $currency->id,
1365 notes => 'test_ap_currency_tax_not_included_and_payment credit note 0.8 + 1.33333',
1366 invnumber => 'test_ap_currency_tax_not_included_and_payment credit note 0.8 + 1.33333',
1368 $invoice->add_ap_amount_row(
1369 amount => $invoice->netamount,
1370 chart => $ap_amount_chart,
1371 tax_id => $tax_9->id,
1374 $invoice->create_ap_row(chart => $ap_chart);
1377 is($invoice->currency_id , $currency->id , "$title: currency_id has been saved");
1378 is($invoice->netamount , -100 , "$title: ap amount has been converted");
1379 is($invoice->amount , -119 , "$title: ap amount has been converted");
1380 is($invoice->taxincluded , 0 , "$title: ap transaction doesn\'t have taxincluded");
1381 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');
1382 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');
1384 $invoice->pay_invoice(chart_id => $bank->id,
1387 transdate => $transdate2->to_kivitendo,
1389 $invoice->pay_invoice(chart_id => $bank->id,
1392 transdate => $transdate3->to_kivitendo,
1394 $invoice->pay_invoice(chart_id => $bank->id,
1397 transdate => $transdate4->to_kivitendo,
1399 my $fx_transactions = SL::DB::Manager::AccTransaction->get_all(where => [ trans_id => $invoice->id, fx_transaction => 1 ], sort_by => ('acc_trans_id'));
1400 is(scalar @{$fx_transactions}, 3, "$title: ap transaction has 3 fx transactions");
1401 is($fx_transactions->[0]->amount, '2.00000', "$title: fx transaction 1: 10.00-( 10.00*0.80000) = 2.00000");
1402 is($fx_transactions->[1]->amount, '-68.59000', "$title: fx transaction 2: 123.45-(123.45*1.55557) = -68.58511");
1403 is($fx_transactions->[2]->amount, '3.40000', "$title: fx transaction 3: 15.30-(15.30 *0.77777) = 3.40012");
1405 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'));
1406 is($fx_gain_loss_transactions->[0]->amount, '93.28000', "$title: fx gain loss amount ok");
1408 is(scalar @{$invoice->transactions}, 14, "$title: ap transaction has 14 transactions (incl. fxtransactions and gain_loss)");
1409 is($invoice->paid, $invoice->amount, "$title: ap transaction paid = amount in default currency");
1410 is(total_amount($invoice), 0, "$title: even balance");
1413 Support::TestSetup::login();
1415 # test cases: without_skonto
1416 test_default_invoice_one_item_19_without_skonto();
1417 test_default_invoice_two_items_19_7_tax_with_skonto();
1418 test_default_invoice_two_items_19_7_without_skonto();
1419 test_default_invoice_two_items_19_7_without_skonto_incomplete_payment();
1420 test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments();
1421 test_default_purchase_invoice_two_charts_19_7_without_skonto();
1422 test_default_purchase_invoice_two_charts_19_7_tax_partial_unrounded_payment_without_skonto();
1423 test_default_invoice_one_item_19_without_skonto_overpaid();
1425 # test cases: difference_as_skonto
1426 test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto();
1427 test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto_1cent();
1428 test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto_2cent();
1429 test_default_invoice_one_item_19_multiple_payment_final_difference_as_skonto();
1430 test_default_invoice_one_item_19_multiple_payment_final_difference_as_skonto_1cent();
1431 test_default_purchase_invoice_two_charts_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto();
1433 # test cases: with_skonto_pt
1434 test_default_invoice_two_items_19_7_tax_with_skonto_50_50();
1435 test_default_invoice_four_items_19_7_tax_with_skonto_4x_25();
1436 test_default_invoice_four_items_19_7_tax_with_skonto_4x_25_multiple();
1437 test_default_purchase_invoice_two_charts_19_7_with_skonto();
1438 test_default_invoice_four_items_19_7_tax_with_skonto_4x_25_tax_included();
1439 test_default_invoice_two_items_19_7_tax_with_skonto_tax_included();
1441 # test payment of ar and ap transactions with currency and tax included/not included
1442 # exchangerate = 1.33333
1443 test_ar_currency_tax_not_included_and_payment();
1444 test_ar_currency_tax_included();
1445 test_ap_currency_tax_not_included_and_payment();
1446 test_ap_currency_tax_included();
1448 test_ar_currency_tax_not_included_and_payment_2(); # exchangerate 0.8
1449 test_ar_currency_tax_not_included_and_payment_2_credit_note(); # exchangerate 0.8
1451 test_ap_currency_tax_not_included_and_payment_2(); # two exchangerates, with fx_gain_loss
1452 test_ap_currency_tax_not_included_and_payment_2_credit_note(); # two exchangerates, with fx_gain_loss
1454 { local $TODO = "currently this test fails because the code writing the invoice is buggy, the calculation of skonto is correct";
1455 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");
1458 # remove all created data at end of test