epic-ts
[kivitendo-erp.git] / t / db_helper / payment.t
1 use Test::More;
2
3 use strict;
4
5 use lib 't';
6 use utf8;
7
8 use Carp;
9 use Support::TestSetup;
10 use Test::Exception;
11 use List::Util qw(sum);
12
13 use SL::DB::Buchungsgruppe;
14 use SL::DB::Currency;
15 use SL::DB::Customer;
16 use SL::DB::Vendor;
17 use SL::DB::Employee;
18 use SL::DB::Invoice;
19 use SL::DB::Part;
20 use SL::DB::Unit;
21 use SL::DB::TaxZone;
22 use SL::DB::BankAccount;
23 use SL::DB::PaymentTerm;
24
25 my ($customer, $vendor, $currency_id, @parts, $buchungsgruppe, $buchungsgruppe7, $unit, $employee, $tax, $tax7, $taxzone, $payment_terms, $bank_account);
26
27 my $ALWAYS_RESET = 1;
28
29 my $reset_state_counter = 0;
30
31 my $purchase_invoice_counter = 0; # used for generating purchase invnumber
32
33 sub clear_up {
34   SL::DB::Manager::InvoiceItem->delete_all(all => 1);
35   SL::DB::Manager::Invoice->delete_all(all => 1);
36   SL::DB::Manager::PurchaseInvoice->delete_all(all => 1);
37   SL::DB::Manager::Part->delete_all(all => 1);
38   SL::DB::Manager::Customer->delete_all(all => 1);
39   SL::DB::Manager::Vendor->delete_all(all => 1);
40   SL::DB::Manager::BankAccount->delete_all(all => 1);
41   SL::DB::Manager::PaymentTerm->delete_all(all => 1);
42 };
43
44 sub reset_state {
45   my %params = @_;
46
47   return if $reset_state_counter;
48
49   $params{$_} ||= {} for qw(buchungsgruppe unit customer part tax vendor);
50
51   clear_up();
52
53
54   $buchungsgruppe  = SL::DB::Manager::Buchungsgruppe->find_by(description => 'Standard 19%', %{ $params{buchungsgruppe} }) || croak "No accounting group";
55   $buchungsgruppe7 = SL::DB::Manager::Buchungsgruppe->find_by(description => 'Standard 7%')                                || croak "No accounting group for 7\%";
56   $unit            = SL::DB::Manager::Unit->find_by(name => 'kg', %{ $params{unit} })                                      || croak "No unit";
57   $employee        = SL::DB::Manager::Employee->current                                                                    || croak "No employee";
58   $tax             = SL::DB::Manager::Tax->find_by(taxkey => 3, rate => 0.19, %{ $params{tax} })                           || croak "No tax";
59   $tax7            = SL::DB::Manager::Tax->find_by(taxkey => 2, rate => 0.07)                                              || croak "No tax for 7\%";
60   $taxzone         = SL::DB::Manager::TaxZone->find_by( description => 'Inland')                                           || croak "No taxzone";
61
62   $currency_id     = $::instance_conf->get_currency_id;
63
64   $customer     = SL::DB::Customer->new(
65     name        => 'Test Customer',
66     currency_id => $currency_id,
67     taxzone_id  => $taxzone->id,
68     %{ $params{customer} }
69   )->save;
70
71   $bank_account     =  SL::DB::BankAccount->new(
72     account_number  => '123',
73     bank_code       => '123',
74     iban            => '123',
75     bic             => '123',
76     bank            => '123',
77     chart_id        => SL::DB::Manager::Chart->find_by( description => 'Bank' )->id,
78     name            => SL::DB::Manager::Chart->find_by( description => 'Bank' )->description,
79   )->save;
80
81   $payment_terms     =  SL::DB::PaymentTerm->new(
82     description      => 'payment',
83     description_long => 'payment',
84     terms_netto      => '30',
85     terms_skonto     => '5',
86     percent_skonto   => '0.05',
87     auto_calculation => 1,
88   )->save;
89
90   $vendor       = SL::DB::Vendor->new(
91     name        => 'Test Vendor',
92     currency_id => $currency_id,
93     taxzone_id  => $taxzone->id,
94     payment_id  => $payment_terms->id,
95     %{ $params{vendor} }
96   )->save;
97
98
99   @parts = ();
100   push @parts, SL::DB::Part->new(
101     partnumber         => 'T4254',
102     description        => 'Fourty-two fifty-four',
103     lastcost           => 1.93,
104     sellprice          => 2.34,
105     buchungsgruppen_id => $buchungsgruppe->id,
106     unit               => $unit->name,
107     %{ $params{part1} }
108   )->save;
109
110   push @parts, SL::DB::Part->new(
111     partnumber         => 'T0815',
112     description        => 'Zero EIGHT fifteeN @ 7%',
113     lastcost           => 5.473,
114     sellprice          => 9.714,
115     buchungsgruppen_id => $buchungsgruppe7->id,
116     unit               => $unit->name,
117     %{ $params{part2} }
118   )->save;
119   push @parts, SL::DB::Part->new(
120     partnumber         => '19%',
121     description        => 'Testware 19%',
122     lastcost           => 0,
123     sellprice          => 50,
124     buchungsgruppen_id => $buchungsgruppe->id,
125     unit               => $unit->name,
126     %{ $params{part3} }
127   )->save;
128   push @parts, SL::DB::Part->new(
129     partnumber         => '7%',
130     description        => 'Testware 7%',
131     lastcost           => 0,
132     sellprice          => 50,
133     buchungsgruppen_id => $buchungsgruppe7->id,
134     unit               => $unit->name,
135     %{ $params{part4} }
136   )->save;
137
138   $reset_state_counter++;
139 }
140
141 sub new_invoice {
142   my %params  = @_;
143
144   return SL::DB::Invoice->new(
145     customer_id => $customer->id,
146     currency_id => $currency_id,
147     employee_id => $employee->id,
148     salesman_id => $employee->id,
149     gldate      => DateTime->today_local->to_kivitendo,
150     taxzone_id  => $taxzone->id,
151     transdate   => DateTime->today_local->to_kivitendo,
152     invoice     => 1,
153     type        => 'invoice',
154     %params,
155   );
156
157 }
158
159 sub new_purchase_invoice {
160   # my %params  = @_;
161   # manually create a Kreditorenbuchung from scratch, ap + acc_trans bookings, as no helper exists yet, like $invoice->post.
162   # arap-Booking must come last in the acc_trans order
163   $purchase_invoice_counter++;
164
165   my $purchase_invoice = SL::DB::PurchaseInvoice->new(
166     vendor_id   => $vendor->id,
167     invnumber   => 'newap ' . $purchase_invoice_counter ,
168     currency_id => $currency_id,
169     employee_id => $employee->id,
170     gldate      => DateTime->today_local->to_kivitendo,
171     taxzone_id  => $taxzone->id,
172     transdate   => DateTime->today_local->to_kivitendo,
173     invoice     => 0,
174     type        => 'invoice',
175     taxincluded => 0,
176     amount      => '226',
177     netamount   => '200',
178     paid        => '0',
179     # %params,
180   )->save;
181
182   my $today = DateTime->today_local->to_kivitendo;
183   my $expense_chart  = SL::DB::Manager::Chart->find_by(accno => '3400');
184   my $expense_chart_booking= SL::DB::AccTransaction->new(
185                                         trans_id   => $purchase_invoice->id,
186                                         chart_id   => $expense_chart->id,
187                                         chart_link => $expense_chart->link,
188                                         amount     => '-100',
189                                         transdate  => $today,
190                                         source     => '',
191                                         taxkey     => 9,
192                                         tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 9)->id);
193   $expense_chart_booking->save;
194
195   my $tax_chart  = SL::DB::Manager::Chart->find_by(accno => '1576');
196   my $tax_chart_booking= SL::DB::AccTransaction->new(
197                                         trans_id   => $purchase_invoice->id,
198                                         chart_id   => $tax_chart->id,
199                                         chart_link => $tax_chart->link,
200                                         amount     => '-19',
201                                         transdate  => $today,
202                                         source     => '',
203                                         taxkey     => 0,
204                                         tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 9)->id);
205   $tax_chart_booking->save;
206   $expense_chart  = SL::DB::Manager::Chart->find_by(accno => '3300');
207   $expense_chart_booking= SL::DB::AccTransaction->new(
208                                         trans_id   => $purchase_invoice->id,
209                                         chart_id   => $expense_chart->id,
210                                         chart_link => $expense_chart->link,
211                                         amount     => '-100',
212                                         transdate  => $today,
213                                         source     => '',
214                                         taxkey     => 8,
215                                         tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 8)->id);
216   $expense_chart_booking->save;
217
218
219   $tax_chart  = SL::DB::Manager::Chart->find_by(accno => '1571');
220   $tax_chart_booking= SL::DB::AccTransaction->new(
221                                          trans_id   => $purchase_invoice->id,
222                                          chart_id   => $tax_chart->id,
223                                          chart_link => $tax_chart->link,
224                                          amount     => '-7',
225                                          transdate  => $today,
226                                          source     => '',
227                                          taxkey     => 0,
228                                          tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 8)->id);
229   $tax_chart_booking->save;
230   my $arap_chart  = SL::DB::Manager::Chart->find_by(accno => '1600');
231   my $arap_booking= SL::DB::AccTransaction->new(trans_id   => $purchase_invoice->id,
232                                                 chart_id   => $arap_chart->id,
233                                                 chart_link => $arap_chart->link,
234                                                 amount     => '226',
235                                                 transdate  => $today,
236                                                 source     => '',
237                                                 taxkey     => 0,
238                                                 tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 0)->id);
239   $arap_booking->save;
240
241   return $purchase_invoice;
242 }
243
244 sub new_item {
245   my (%params) = @_;
246
247   my $part = delete($params{part}) || $parts[0];
248
249   return SL::DB::InvoiceItem->new(
250     parts_id    => $part->id,
251     lastcost    => $part->lastcost,
252     sellprice   => $part->sellprice,
253     description => $part->description,
254     unit        => $part->unit,
255     %params,
256   );
257 }
258
259 sub number_of_payments {
260   my $transactions = shift;
261
262   my $number_of_payments;
263   my $paid_amount;
264   foreach my $transaction ( @$transactions ) {
265     if ( $transaction->chart_link =~ /(AR_paid|AP_paid)/ ) {
266       $paid_amount += $transaction->amount ;
267       $number_of_payments++;
268     };
269   };
270   return ($number_of_payments, $paid_amount);
271 };
272
273 sub total_amount {
274   my $transactions = shift;
275
276   my $total = sum map { $_->amount } @$transactions;
277
278   return $::form->round_amount($total, 5);
279
280 };
281
282
283 # test 1
284 sub test_default_invoice_one_item_19_without_skonto() {
285   reset_state() if $ALWAYS_RESET;
286
287   my $item    = new_item(qty => 2.5);
288   my $invoice = new_invoice(
289     taxincluded  => 0,
290     invoiceitems => [ $item ],
291     payment_id   => $payment_terms->id,
292   );
293   $invoice->post;
294
295   my $purchase_invoice = new_purchase_invoice();
296
297
298   # default values
299   my %params = ( chart_id => $bank_account->chart_id,
300                  transdate => DateTime->today_local->to_kivitendo
301                );
302
303   $params{amount} = '6.96';
304   $params{payment_type} = 'without_skonto';
305
306   $invoice->pay_invoice( %params );
307
308   my ($number_of_payments, $paid_amount) = number_of_payments($invoice->transactions);
309   my $total = total_amount($invoice->transactions);
310
311   my $title = 'default invoice, one item, 19% tax, without_skonto';
312
313   is($invoice->netamount,   5.85,      "${title}: netamount");
314   is($invoice->amount,      6.96,      "${title}: amount");
315   is($paid_amount,         -6.96,      "${title}: paid amount");
316   is($number_of_payments,      1,      "${title}: 1 AR_paid booking");
317   is($invoice->paid,        6.96,      "${title}: paid");
318   is($total,                   0,      "${title}: even balance");
319
320 }
321
322 sub test_default_invoice_one_item_19_without_skonto_overpaid() {
323   reset_state() if $ALWAYS_RESET;
324
325   my $item    = new_item(qty => 2.5);
326   my $invoice = new_invoice(
327     taxincluded  => 0,
328     invoiceitems => [ $item ],
329     payment_id   => $payment_terms->id,
330   );
331   $invoice->post;
332
333   my $purchase_invoice = new_purchase_invoice();
334
335
336   # default values
337   my %params = ( chart_id => $bank_account->chart_id,
338                  transdate => DateTime->today_local->to_kivitendo
339                );
340
341   $params{amount} = '16.96';
342   $params{payment_type} = 'without_skonto';
343   $invoice->pay_invoice( %params );
344
345   $params{amount} = '-10.00';
346   $invoice->pay_invoice( %params );
347
348   my ($number_of_payments, $paid_amount) = number_of_payments($invoice->transactions);
349   my $total = total_amount($invoice->transactions);
350
351   my $title = 'default invoice, one item, 19% tax, without_skonto';
352
353   is($invoice->netamount,   5.85,      "${title}: netamount");
354   is($invoice->amount,      6.96,      "${title}: amount");
355   is($paid_amount,         -6.96,      "${title}: paid amount");
356   is($number_of_payments,      2,      "${title}: 1 AR_paid booking");
357   is($invoice->paid,        6.96,      "${title}: paid");
358   is($total,                   0,      "${title}: even balance");
359
360 }
361
362
363 # test 2
364 sub test_default_invoice_two_items_19_7_tax_with_skonto() {
365   reset_state() if $ALWAYS_RESET;
366
367   my $item1   = new_item(qty => 2.5);
368   my $item2   = new_item(qty => 1.2, part => $parts[1]);
369   my $invoice = new_invoice(
370     taxincluded  => 0,
371     invoiceitems => [ $item1, $item2 ],
372     payment_id  => $payment_terms->id,
373   );
374   $invoice->post;
375
376   # default values
377   my %params = ( chart_id => $bank_account->chart_id,
378                  transdate => DateTime->today_local->to_kivitendo
379                );
380
381   $params{payment_type} = 'with_skonto_pt';
382   $params{amount}       = $invoice->amount_less_skonto;
383
384   $invoice->pay_invoice( %params );
385
386   my ($number_of_payments, $paid_amount) = number_of_payments($invoice->transactions);
387   my $total = total_amount($invoice->transactions);
388
389   my $title = 'default invoice, two items, 19/7% tax with_skonto_pt';
390
391   is($invoice->netamount,  5.85 + 11.66,   "${title}: netamount");
392   is($invoice->amount,     6.96 + 12.48,   "${title}: amount");
393   is($paid_amount,               -19.44,   "${title}: paid amount");
394   is($invoice->paid,              19.44,   "${title}: paid");
395   is($number_of_payments,             3,   "${title}: 3 AR_paid bookings");
396   is($total,                          0,   "${title}: even balance");
397 }
398
399 sub test_default_invoice_two_items_19_7_tax_with_skonto_tax_included() {
400   reset_state() if $ALWAYS_RESET;
401
402   my $item1   = new_item(qty => 2.5);
403   my $item2   = new_item(qty => 1.2, part => $parts[1]);
404   my $invoice = new_invoice(
405     taxincluded  => 1,
406     invoiceitems => [ $item1, $item2 ],
407     payment_id  => $payment_terms->id,
408   );
409   $invoice->post;
410
411   # default values
412   my %params = ( chart_id => $bank_account->chart_id,
413                  transdate => DateTime->today_local->to_kivitendo
414                );
415
416   $params{payment_type} = 'with_skonto_pt';
417   $params{amount}       = $invoice->amount_less_skonto;
418
419   $invoice->pay_invoice( %params );
420
421   my ($number_of_payments, $paid_amount) = number_of_payments($invoice->transactions);
422   my $total = total_amount($invoice->transactions);
423
424   my $title = 'default invoice, two items, 19/7% tax with_skonto_pt';
425
426   is($invoice->netamount,         15.82,   "${title}: netamount");
427   is($invoice->amount,            17.51,   "${title}: amount");
428   is($paid_amount,               -17.51,   "${title}: paid amount");
429   is($invoice->paid,              17.51,   "${title}: paid");
430   is($number_of_payments,             3,   "${title}: 3 AR_paid bookings");
431   { local $TODO = "currently this test fails because the code writing the invoice is buggy, the calculation of skonto is correct";
432   is($total,                          0,   "${title}: even balance");
433   }
434 }
435
436 # test 3 : two items, without skonto
437 sub test_default_invoice_two_items_19_7_without_skonto() {
438   reset_state() if $ALWAYS_RESET;
439
440   my $item1   = new_item(qty => 2.5);
441   my $item2   = new_item(qty => 1.2, part => $parts[1]);
442   my $invoice = new_invoice(
443     taxincluded  => 0,
444     invoiceitems => [ $item1, $item2 ],
445     payment_id  => $payment_terms->id,
446   );
447   $invoice->post;
448
449   # default values
450   my %params = ( chart_id => $bank_account->chart_id,
451                  transdate => DateTime->today_local->to_kivitendo
452                );
453
454   $params{amount} = '19.44'; # pass full amount
455   $params{payment_type} = 'without_skonto';
456
457   $invoice->pay_invoice( %params );
458
459   my ($number_of_payments, $paid_amount) = number_of_payments($invoice->transactions);
460   my $total = total_amount($invoice->transactions);
461
462   my $title = 'default invoice, two items, 19/7% tax without skonto';
463
464   is($invoice->netamount,     5.85 + 11.66,     "${title}: netamount");
465   is($invoice->amount,        6.96 + 12.48,     "${title}: amount");
466   is($paid_amount,                  -19.44,     "${title}: paid amount");
467   is($invoice->paid,                 19.44,     "${title}: paid");
468   is($number_of_payments,                1,     "${title}: 1 AR_paid bookings");
469   is($total,                             0,     "${title}: even balance");
470 }
471
472 # test 4
473 sub test_default_invoice_two_items_19_7_without_skonto_incomplete_payment() {
474   reset_state() if $ALWAYS_RESET;
475
476   my $item1   = new_item(qty => 2.5);
477   my $item2   = new_item(qty => 1.2, part => $parts[1]);
478   my $invoice = new_invoice(
479     taxincluded  => 0,
480     invoiceitems => [ $item1, $item2 ],
481     payment_id  => $payment_terms->id,
482   );
483   $invoice->post;
484
485   $invoice->pay_invoice( amount       => '9.44',
486                          payment_type => 'without_skonto',
487                          chart_id     => $bank_account->chart_id,
488                          transdate    => DateTime->today_local->to_kivitendo,
489                        );
490
491   my ($number_of_payments, $paid_amount) = number_of_payments($invoice->transactions);
492   my $total = total_amount($invoice->transactions);
493
494   my $title = 'default invoice, two items, 19/7% tax without skonto incomplete payment';
495
496   is($invoice->netamount,        5.85 + 11.66,     "${title}: netamount");
497   is($invoice->amount,           6.96 + 12.48,     "${title}: amount");
498   is($paid_amount,              -9.44,             "${title}: paid amount");
499   is($invoice->paid,             9.44,            "${title}: paid");
500   is($number_of_payments,   1,                "${title}: 1 AR_paid bookings");
501   is($total,                    0,                "${title}: even balance");
502 }
503
504 # test 5
505 sub test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments() {
506   reset_state() if $ALWAYS_RESET;
507
508   my $item1   = new_item(qty => 2.5);
509   my $item2   = new_item(qty => 1.2, part => $parts[1]);
510   my $invoice = new_invoice(
511     taxincluded  => 0,
512     invoiceitems => [ $item1, $item2 ],
513     payment_id  => $payment_terms->id,
514   );
515   $invoice->post;
516
517   $invoice->pay_invoice( amount       => '9.44',
518                          payment_type => 'without_skonto',
519                          chart_id     => $bank_account->chart_id,
520                          transdate    => DateTime->today_local->to_kivitendo
521                        );
522   $invoice->pay_invoice( amount       => '10.00',
523                          chart_id     => $bank_account->chart_id,
524                          transdate    => DateTime->today_local->to_kivitendo
525                        );
526
527   my ($number_of_payments, $paid_amount) = number_of_payments($invoice->transactions);
528   my $total = total_amount($invoice->transactions);
529
530   my $title = 'default invoice, two items, 19/7% tax not included';
531
532   is($invoice->netamount,        5.85 + 11.66,     "${title}: netamount");
533   is($invoice->amount,           6.96 + 12.48,     "${title}: amount");
534   is($paid_amount,                     -19.44,     "${title}: paid amount");
535   is($invoice->paid,                    19.44,     "${title}: paid");
536   is($number_of_payments,                   2,     "${title}: 2 AR_paid bookings");
537   is($total,                                0,     "${title}: even balance");
538
539 }
540
541 # test 6
542 sub test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto() {
543   reset_state() if $ALWAYS_RESET;
544
545   my $item1   = new_item(qty => 2.5);
546   my $item2   = new_item(qty => 1.2, part => $parts[1]);
547   my $invoice = new_invoice(
548     taxincluded  => 0,
549     invoiceitems => [ $item1, $item2 ],
550     payment_id  => $payment_terms->id,
551   );
552   $invoice->post;
553
554   $invoice->pay_invoice( amount       => '9.44',
555                          payment_type => 'without_skonto',
556                          chart_id     => $bank_account->chart_id,
557                          transdate    => DateTime->today_local->to_kivitendo
558                        );
559   $invoice->pay_invoice( amount       => '8.73',
560                          payment_type => 'without_skonto',
561                          chart_id     => $bank_account->chart_id,
562                          transdate    => DateTime->today_local->to_kivitendo
563                        );
564   $invoice->pay_invoice( amount       => $invoice->open_amount,
565                          payment_type => 'difference_as_skonto',
566                          chart_id     => $bank_account->chart_id,
567                          transdate    => DateTime->today_local->to_kivitendo
568                        );
569
570   my ($number_of_payments, $paid_amount) = number_of_payments($invoice->transactions);
571   my $total = total_amount($invoice->transactions);
572
573   my $title = 'default invoice, two items, 19/7% tax not included';
574
575   is($invoice->netamount,        5.85 + 11.66,     "${title}: netamount");
576   is($invoice->amount,           6.96 + 12.48,     "${title}: amount");
577   is($paid_amount,                     -19.44,     "${title}: paid amount");
578   is($invoice->paid,                    19.44,     "${title}: paid");
579   is($number_of_payments,                   4,     "${title}: 4 AR_paid bookings");
580   is($total,                                0,     "${title}: even balance");
581
582 }
583
584 sub  test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto_1cent() {
585   reset_state() if $ALWAYS_RESET;
586
587   # if there is only one cent left there can only be one skonto booking, the
588   # error handling should choose the highest amount, which is the 7% account
589   # (11.66) rather than the 19% account (5.85).  The actual tax amount is
590   # higher for the 19% case, though (1.11 compared to 0.82)
591
592   my $item1   = new_item(qty => 2.5);
593   my $item2   = new_item(qty => 1.2, part => $parts[1]);
594   my $invoice = new_invoice(
595     taxincluded  => 0,
596     invoiceitems => [ $item1, $item2 ],
597     payment_id  => $payment_terms->id,
598   );
599   $invoice->post;
600
601   $invoice->pay_invoice( amount       => '19.42',
602                          payment_type => 'without_skonto',
603                          chart_id     => $bank_account->chart_id,
604                          transdate    => DateTime->today_local->to_kivitendo
605                        );
606   $invoice->pay_invoice( amount       => $invoice->open_amount,
607                          payment_type => 'difference_as_skonto',
608                          chart_id     => $bank_account->chart_id,
609                          transdate    => DateTime->today_local->to_kivitendo
610                        );
611
612   my ($number_of_payments, $paid_amount) = number_of_payments($invoice->transactions);
613   my $total = total_amount($invoice->transactions);
614
615   my $title = 'default invoice, two items, 19/7% tax not included';
616
617   is($invoice->netamount,        5.85 + 11.66,     "${title}: netamount");
618   is($invoice->amount,           6.96 + 12.48,     "${title}: amount");
619   is($paid_amount,                     -19.44,     "${title}: paid amount");
620   is($invoice->paid,                    19.44,     "${title}: paid");
621   is($number_of_payments,                   3,     "${title}: 2 AR_paid bookings");
622   is($total,                                0,     "${title}: even balance");
623
624 }
625
626 sub  test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto_2cent() {
627   reset_state() if $ALWAYS_RESET;
628
629   # if there are two cents left there will be two skonto bookings, 1 cent each
630   my $item1   = new_item(qty => 2.5);
631   my $item2   = new_item(qty => 1.2, part => $parts[1]);
632   my $invoice = new_invoice(
633     taxincluded  => 0,
634     invoiceitems => [ $item1, $item2 ],
635     payment_id  => $payment_terms->id,
636   );
637   $invoice->post;
638
639   $invoice->pay_invoice( amount       => '19.42',
640                          payment_type => 'without_skonto',
641                          chart_id     => $bank_account->chart_id,
642                          transdate    => DateTime->today_local->to_kivitendo
643                        );
644   $invoice->pay_invoice( amount       => $invoice->open_amount,
645                          payment_type => 'difference_as_skonto',
646                          chart_id     => $bank_account->chart_id,
647                          transdate    => DateTime->today_local->to_kivitendo
648                        );
649
650   my ($number_of_payments, $paid_amount) = number_of_payments($invoice->transactions);
651   my $total = total_amount($invoice->transactions);
652
653   my $title = 'default invoice, two items, 19/7% tax not included';
654
655   is($invoice->netamount,        5.85 + 11.66,     "${title}: netamount");
656   is($invoice->amount,           6.96 + 12.48,     "${title}: amount");
657   is($paid_amount,                     -19.44,     "${title}: paid amount");
658   is($invoice->paid,                    19.44,     "${title}: paid");
659   is($number_of_payments,                   3,     "${title}: 3 AR_paid bookings");
660   is($total,                                0,     "${title}: even balance");
661
662 }
663
664 sub test_default_invoice_one_item_19_multiple_payment_final_difference_as_skonto() {
665   reset_state() if $ALWAYS_RESET;
666
667   my $item    = new_item(qty => 2.5);
668   my $invoice = new_invoice(
669     taxincluded  => 0,
670     invoiceitems => [ $item ],
671     payment_id   => $payment_terms->id,
672   );
673   $invoice->post;
674
675   # default values
676   my %params = ( chart_id  => $bank_account->chart_id,
677                  transdate => DateTime->today_local->to_kivitendo
678                );
679
680   $params{amount}       = '2.32';
681   $params{payment_type} = 'without_skonto';
682   $invoice->pay_invoice( %params );
683
684   $params{amount}       = '3.81';
685   $params{payment_type} = 'without_skonto';
686   $invoice->pay_invoice( %params );
687
688   $params{amount}       = $invoice->open_amount; # set amount, otherwise previous 3.81 is used
689   $params{payment_type} = 'difference_as_skonto';
690   $invoice->pay_invoice( %params );
691
692   my ($number_of_payments, $paid_amount) = number_of_payments($invoice->transactions);
693   my $total = total_amount($invoice->transactions);
694
695   my $title = 'default invoice, one item, 19% tax, without_skonto';
696
697   is($invoice->netamount,       5.85,     "${title}: netamount");
698   is($invoice->amount,          6.96,     "${title}: amount");
699   is($paid_amount,             -6.96,     "${title}: paid amount");
700   is($number_of_payments,          3,     "${title}: 3 AR_paid booking");
701   is($invoice->paid,            6.96,     "${title}: paid");
702   is($total,                       0,     "${title}: even balance");
703
704 }
705
706 sub test_default_invoice_one_item_19_multiple_payment_final_difference_as_skonto_1cent() {
707   reset_state() if $ALWAYS_RESET;
708
709   my $item    = new_item(qty => 2.5);
710   my $invoice = new_invoice(
711     taxincluded  => 0,
712     invoiceitems => [ $item ],
713     payment_id   => $payment_terms->id,
714   );
715   $invoice->post;
716
717   # default values
718   my %params = ( chart_id  => $bank_account->chart_id,
719                  transdate => DateTime->today_local->to_kivitendo
720                );
721
722   $params{amount}       = '6.95';
723   $params{payment_type} = 'without_skonto';
724   $invoice->pay_invoice( %params );
725
726   $params{amount}       = $invoice->open_amount; # set amount, otherwise previous value 6.95 is used
727   $params{payment_type} = 'difference_as_skonto';
728   $invoice->pay_invoice( %params );
729
730   my ($number_of_payments, $paid_amount) = number_of_payments($invoice->transactions);
731   my $total = total_amount($invoice->transactions);
732
733   my $title = 'default invoice, one item, 19% tax, without_skonto';
734
735   is($invoice->netamount,       5.85,     "${title}: netamount");
736   is($invoice->amount,          6.96,     "${title}: amount");
737   is($paid_amount,             -6.96,     "${title}: paid amount");
738   is($number_of_payments,          2,     "${title}: 3 AR_paid booking");
739   is($invoice->paid,            6.96,     "${title}: paid");
740   is($total,                       0,     "${title}: even balance");
741
742 }
743
744 # test 3 : two items, without skonto
745 sub test_default_purchase_invoice_two_charts_19_7_without_skonto() {
746   reset_state() if $ALWAYS_RESET;
747
748   my $purchase_invoice = new_purchase_invoice();
749
750   my %params = ( chart_id => $bank_account->chart_id,
751                  transdate => DateTime->today_local->to_kivitendo
752                );
753
754   $params{amount} = '226'; # pass full amount
755   $params{payment_type} = 'without_skonto';
756
757   $purchase_invoice->pay_invoice( %params );
758
759   my ($number_of_payments, $paid_amount) = number_of_payments($purchase_invoice->transactions);
760   my $total = total_amount($purchase_invoice->transactions);
761
762   my $title = 'default invoice, two items, 19/7% tax without skonto';
763
764   is($paid_amount,         226,     "${title}: paid amount");
765   is($number_of_payments,    1,     "${title}: 1 AP_paid bookings");
766   is($total,                 0,     "${title}: even balance");
767
768 }
769
770 sub test_default_purchase_invoice_two_charts_19_7_with_skonto() {
771   reset_state() if $ALWAYS_RESET;
772
773   my $purchase_invoice = new_purchase_invoice();
774
775   my %params = ( chart_id => $bank_account->chart_id,
776                  transdate => DateTime->today_local->to_kivitendo
777                );
778
779   # $params{amount} = '226'; # pass full amount
780   $params{payment_type} = 'with_skonto_pt';
781
782   $purchase_invoice->pay_invoice( %params );
783
784   my ($number_of_payments, $paid_amount) = number_of_payments($purchase_invoice->transactions);
785   my $total = total_amount($purchase_invoice->transactions);
786
787   my $title = 'default invoice, two items, 19/7% tax without skonto';
788
789   is($paid_amount,         226,     "${title}: paid amount");
790   is($number_of_payments,    3,     "${title}: 1 AP_paid bookings");
791   is($total,                 0,     "${title}: even balance");
792
793 }
794
795 sub test_default_purchase_invoice_two_charts_19_7_tax_partial_unrounded_payment_without_skonto() {
796   # check whether unrounded amounts passed via $params{amount} are rounded for without_skonto case
797   reset_state() if $ALWAYS_RESET;
798   my $purchase_invoice = new_purchase_invoice();
799   $purchase_invoice->pay_invoice(
800                           amount       => ( $purchase_invoice->amount / 3 * 2),
801                           payment_type => 'without_skonto',
802                           chart_id     => $bank_account->chart_id,
803                           transdate    => DateTime->today_local->to_kivitendo
804                          );
805   my ($number_of_payments, $paid_amount) = number_of_payments($purchase_invoice->transactions);
806   my $total = total_amount($purchase_invoice->transactions);
807
808   my $title = 'default purchase_invoice, two charts, 19/7% tax multiple payments with final difference as skonto';
809
810   is($paid_amount,         150.67,   "${title}: paid amount");
811   is($number_of_payments,       1,   "${title}: 1 AP_paid bookings");
812   is($total,                    0,   "${title}: even balance");
813 };
814
815
816 sub test_default_purchase_invoice_two_charts_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto() {
817   reset_state() if $ALWAYS_RESET;
818
819   my $purchase_invoice = new_purchase_invoice();
820
821   # pay 2/3 and 1/5, leaves 3.83% to be used as Skonto
822   $purchase_invoice->pay_invoice(
823                           amount       => ( $purchase_invoice->amount / 3 * 2),
824                           payment_type => 'without_skonto',
825                           chart_id     => $bank_account->chart_id,
826                           transdate    => DateTime->today_local->to_kivitendo
827                          );
828   $purchase_invoice->pay_invoice(
829                           amount       => ( $purchase_invoice->amount / 5 ),
830                           payment_type => 'without_skonto',
831                           chart_id     => $bank_account->chart_id,
832                           transdate    => DateTime->today_local->to_kivitendo
833                          );
834   $purchase_invoice->pay_invoice(
835                           payment_type => 'difference_as_skonto',
836                           chart_id     => $bank_account->chart_id,
837                           transdate    => DateTime->today_local->to_kivitendo
838                          );
839
840   my ($number_of_payments, $paid_amount) = number_of_payments($purchase_invoice->transactions);
841   my $total = total_amount($purchase_invoice->transactions);
842
843   my $title = 'default purchase_invoice, two charts, 19/7% tax multiple payments with final difference as skonto';
844
845   is($paid_amount,         226, "${title}: paid amount");
846   is($number_of_payments,    4, "${title}: 1 AP_paid bookings");
847   is($total,                 0, "${title}: even balance");
848
849 }
850
851 # test
852 sub test_default_invoice_two_items_19_7_tax_with_skonto_50_50() {
853   reset_state() if $ALWAYS_RESET;
854
855   my $item1   = new_item(qty => 1, part => $parts[2]);
856   my $item2   = new_item(qty => 1, part => $parts[3]);
857   my $invoice = new_invoice(
858     taxincluded  => 0,
859     invoiceitems => [ $item1, $item2 ],
860     payment_id  => $payment_terms->id,
861   );
862   $invoice->post;
863
864   # default values
865   my %params = ( chart_id => $bank_account->chart_id,
866                  transdate => DateTime->today_local->to_kivitendo
867                );
868
869   $params{amount} = $invoice->amount_less_skonto;
870   $params{payment_type} = 'with_skonto_pt';
871
872   $invoice->pay_invoice( %params );
873
874   my ($number_of_payments, $paid_amount) = number_of_payments($invoice->transactions);
875   my $total = total_amount($invoice->transactions);
876
877   my $title = 'default invoice, two items, 19/7% tax with_skonto_pt 50/50';
878
879   is($invoice->netamount,        100,     "${title}: netamount");
880   is($invoice->amount,           113,     "${title}: amount");
881   is($paid_amount,              -113,     "${title}: paid amount");
882   is($invoice->paid,             113,     "${title}: paid");
883   is($number_of_payments,          3,     "${title}: 3 AR_paid bookings");
884   is($total,                       0,     "${title}: even balance");
885 }
886
887 # test
888 sub test_default_invoice_four_items_19_7_tax_with_skonto_4x_25() {
889   reset_state() if $ALWAYS_RESET;
890
891   my $item1   = new_item(qty => 0.5, part => $parts[2]);
892   my $item2   = new_item(qty => 0.5, part => $parts[3]);
893   my $item3   = new_item(qty => 0.5, part => $parts[2]);
894   my $item4   = new_item(qty => 0.5, part => $parts[3]);
895   my $invoice = new_invoice(
896     taxincluded  => 0,
897     invoiceitems => [ $item1, $item2, $item3, $item4 ],
898     payment_id  => $payment_terms->id,
899   );
900   $invoice->post;
901
902   # default values
903   my %params = ( chart_id => $bank_account->chart_id,
904                  transdate => DateTime->today_local->to_kivitendo
905                );
906
907   $params{amount} = $invoice->amount_less_skonto;
908   $params{payment_type} = 'with_skonto_pt';
909
910   $invoice->pay_invoice( %params );
911
912   my ($number_of_payments, $paid_amount) = number_of_payments($invoice->transactions);
913   my $total = total_amount($invoice->transactions);
914
915   my $title = 'default invoice, four items, 19/7% tax with_skonto_pt 4x25';
916
917   is($invoice->netamount , 100  , "${title}: netamount");
918   is($invoice->amount    , 113  , "${title}: amount");
919   is($paid_amount        , -113 , "${title}: paid amount");
920   is($invoice->paid      , 113  , "${title}: paid");
921   is($number_of_payments , 3    , "${title}: 3 AR_paid bookings");
922   is($total              , 0    , "${title}: even balance");
923 }
924
925 sub test_default_invoice_four_items_19_7_tax_with_skonto_4x_25_tax_included() {
926   reset_state() if $ALWAYS_RESET;
927
928   my $item1   = new_item(qty => 0.5, part => $parts[2]);
929   my $item2   = new_item(qty => 0.5, part => $parts[3]);
930   my $item3   = new_item(qty => 0.5, part => $parts[2]);
931   my $item4   = new_item(qty => 0.5, part => $parts[3]);
932   my $invoice = new_invoice(
933     taxincluded  => 1,
934     invoiceitems => [ $item1, $item2, $item3, $item4 ],
935     payment_id  => $payment_terms->id,
936   );
937   $invoice->post;
938
939   # default values
940   my %params = ( chart_id => $bank_account->chart_id,
941                  transdate => DateTime->today_local->to_kivitendo
942                );
943
944   $params{amount} = $invoice->amount_less_skonto;
945   $params{payment_type} = 'with_skonto_pt';
946
947   $invoice->pay_invoice( %params );
948
949   my ($number_of_payments, $paid_amount) = number_of_payments($invoice->transactions);
950   my $total = total_amount($invoice->transactions);
951
952   my $title = 'default invoice, four items, 19/7% tax with_skonto_pt 4x25';
953
954   is($invoice->netamount,   88.75,    "${title}: netamount");
955   is($invoice->amount,        100,    "${title}: amount");
956   is($paid_amount,           -100,    "${title}: paid amount");
957   is($invoice->paid,          100,    "${title}: paid");
958   is($number_of_payments,       3,    "${title}: 3 AR_paid bookings");
959   { local $TODO = "currently this test fails because the code writing the invoice is buggy, the calculation of skonto is correct";
960   is($total,                    0,    "${title}: even balance");
961   }
962 }
963
964 sub test_default_invoice_four_items_19_7_tax_with_skonto_4x_25_multiple() {
965   reset_state() if $ALWAYS_RESET;
966
967   my $item1   = new_item(qty => 0.5, part => $parts[2]);
968   my $item2   = new_item(qty => 0.5, part => $parts[3]);
969   my $item3   = new_item(qty => 0.5, part => $parts[2]);
970   my $item4   = new_item(qty => 0.5, part => $parts[3]);
971   my $invoice = new_invoice(
972     taxincluded  => 0,
973     invoiceitems => [ $item1, $item2, $item3, $item4 ],
974     payment_id  => $payment_terms->id,
975   );
976   $invoice->post;
977
978   $invoice->pay_invoice( amount       => '90',
979                          payment_type => 'without_skonto',
980                          chart_id     => $bank_account->chart_id,
981                          transdate => DateTime->today_local->to_kivitendo
982                        );
983   $invoice->pay_invoice( payment_type => 'difference_as_skonto',
984                          chart_id     => $bank_account->chart_id,
985                          transdate    => DateTime->today_local->to_kivitendo
986                        );
987
988   my ($number_of_payments, $paid_amount) = number_of_payments($invoice->transactions);
989   my $total = total_amount($invoice->transactions);
990
991   my $title = 'default invoice, four items, 19/7% tax with_skonto_pt 4x25';
992
993   is($invoice->netamount,  100,     "${title}: netamount");
994   is($invoice->amount,     113,     "${title}: amount");
995   is($paid_amount,        -113,     "${title}: paid amount");
996   is($invoice->paid,       113,     "${title}: paid");
997   is($number_of_payments,    3,     "${title}: 3 AR_paid bookings");
998   is($total,                 0,     "${title}: even balance: this will fail due to rounding error in invoice post, not the skonto");
999 }
1000
1001 Support::TestSetup::login();
1002  # die;
1003
1004 # test cases: without_skonto
1005  test_default_invoice_one_item_19_without_skonto();
1006  test_default_invoice_two_items_19_7_tax_with_skonto();
1007  test_default_invoice_two_items_19_7_without_skonto();
1008  test_default_invoice_two_items_19_7_without_skonto_incomplete_payment();
1009  test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments();
1010  test_default_purchase_invoice_two_charts_19_7_without_skonto();
1011  test_default_purchase_invoice_two_charts_19_7_tax_partial_unrounded_payment_without_skonto();
1012  test_default_invoice_one_item_19_without_skonto_overpaid();
1013
1014 # test cases: difference_as_skonto
1015  test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto();
1016  test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto_1cent();
1017  test_default_invoice_two_items_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto_2cent();
1018  test_default_invoice_one_item_19_multiple_payment_final_difference_as_skonto();
1019  test_default_invoice_one_item_19_multiple_payment_final_difference_as_skonto_1cent();
1020  test_default_purchase_invoice_two_charts_19_7_tax_without_skonto_multiple_payments_final_difference_as_skonto();
1021
1022 # test cases: with_skonto_pt
1023  test_default_invoice_two_items_19_7_tax_with_skonto_50_50();
1024  test_default_invoice_four_items_19_7_tax_with_skonto_4x_25();
1025  test_default_invoice_four_items_19_7_tax_with_skonto_4x_25_multiple();
1026  test_default_purchase_invoice_two_charts_19_7_with_skonto();
1027  test_default_invoice_four_items_19_7_tax_with_skonto_4x_25_tax_included();
1028  test_default_invoice_two_items_19_7_tax_with_skonto_tax_included();
1029
1030 # remove all created data at end of test
1031 clear_up();
1032
1033 done_testing();
1034
1035 1;