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