2c4a5ac3057db8906a6ab7c6ba69fba1e83e1f1e
[kivitendo-erp.git] / t / form / arap.t
1 use strict;
2 use Test::More;
3
4 use lib 't';
5 use Support::TestSetup;
6 use Carp;
7 use Test::Exception;
8
9 no warnings qw(qw);
10
11 # this test tests the functions calculate_arap and calculate_tax in SL/Form.pm
12 # calculate_arap is used for post_invoice in AR and AP
13 # calculate_tax is used in calculate_arap as well as update in ar/ap/gl and post_transaction in gl
14
15 my ($ar_tax_19, $ar_tax_7,$ar_tax_0);
16 my $config = {};
17 $config->{numberformat} = '1.000,00';
18
19 sub reset_state {
20   my %params = @_;
21
22   $params{$_} ||= {} for qw(ar_tax_19 ar_tax_7 ar_tax_0 );
23
24   # delete rowcount lines in form, would be better to reset form completely
25   for my $hv ( 1 .. 10 ) {
26       foreach my $type ( qw(amount tax tax_id tax_chart) ) {
27           delete $::form{"$type\_$hv"};
28       };
29   };
30
31   $ar_tax_19 = SL::DB::Manager::Tax->find_by(taxkey => 3, rate => 0.19, %{ $params{ar_tax_19} })  || croak "No 19% tax";
32   $ar_tax_7  = SL::DB::Manager::Tax->find_by(taxkey => 2, rate => 0.07, %{ $params{ar_tax_7} })   || croak "No 7% tax";
33   $ar_tax_0  = SL::DB::Manager::Tax->find_by(taxkey => 0, rate => 0.00, %{ $params{ar_tax_0} })   || croak "No 0% tax";
34
35 };
36
37 sub arap_test {
38   my ($testcase) = @_;
39
40   reset_state;
41
42   # values from testcase
43   $::form->{taxincluded}     = $testcase->{taxincluded};
44   $::form->{currency}        = $testcase->{currency};
45   $::form->{rowcount}        = scalar @{$testcase->{lines}};
46
47   # parse exchangerate, because it was added in the same numberformat as the
48   # other amounts in the testcases
49   $testcase->{exchangerate}    = $::form->parse_amount(\%::myconfig, $testcase->{exchangerate});
50
51   foreach my $a ( 1 .. scalar @{$testcase->{lines}} ) {
52     my ($taxrate, $form_amount, $netamount, $taxamount, $totalamount) = @{ @{ $testcase->{lines} }[$a-1] };
53     my $tax;
54     if ( $taxrate == 19 ) {
55         $tax = $ar_tax_19;
56     } elsif ( $taxrate == 7 ) {
57         $tax = $ar_tax_7;
58     } elsif ( $taxrate == 0 ) {
59         $tax = $ar_tax_0;
60     } else {
61         croak "illegal taxrate $taxrate";
62     };
63
64     $::form->{"amount_$a"}   = $form_amount;
65     $::form->{"tax_$a"}      = $taxamount;  # tax according to UI, will recalculate anyway?
66     $::form->{"taxchart_$a"} = $tax->id . '--' . $tax->rate;
67
68   };
69
70   # calculate totals using lines in $::form
71   ($::form->{netamount},$::form->{total_tax},$::form->{amount}) = $::form->calculate_arap($testcase->{'buysell'}, $::form->{taxincluded}, $testcase->{'exchangerate'});
72
73   # create tests comparing calculated and expected values
74   is($::form->format_amount(\%::myconfig , $::form->{total_tax} , 2) , $testcase->{'total_taxamount'} , "total tax   = $testcase->{'total_taxamount'}");
75   is($::form->format_amount(\%::myconfig , $::form->{netamount} , 2) , $testcase->{'total_netamount'} , "netamount   = $testcase->{'total_netamount'}");
76   is($::form->format_amount(\%::myconfig , $::form->{amount}    , 2) , $testcase->{'total_amount'}    , "totalamount = $testcase->{'total_amount'}");
77   is($::form->{taxincluded}, $testcase->{'taxincluded'}, "taxincluded = $testcase->{'taxincluded'}");
78
79 };
80
81 sub calculate_tax_test {
82   my ($amount, $rate, $taxincluded, $net, $tax, $total, $dec) = @_;
83   # amount, rate and taxincluded are the values that we want to calculate with
84   # net, tax and total are the values that we expect, dec is the number of decimals we round to
85
86   my ($calculated_net,$calculated_tax) = $::form->calculate_tax($amount,$rate,$taxincluded,$dec);
87
88   is($tax, $calculated_tax, "calculated tax for taxincluded = $taxincluded for net $amount and rate $rate is = $calculated_tax");
89   is($calculated_net, $net, "calculated net for taxincluded = $taxincluded for net $amount and rate $rate is = $net");
90 };
91
92 Support::TestSetup::login();
93
94 # define the various lines that can be used for the testcases
95 # always use positive values for buy/sell, like in the interface
96 #                   tax  input   net      tax   total  type
97 my @testline1  = qw(19   56,53   47,50   9,03   56,53  sell);
98 my @testline2  = qw(19   11,90   10,00   1,90   11,90  sell);
99 my @testline3  = qw( 7   14,39   13,45   0,94   11,90  sell);
100 my @testline4  = qw(19  133,08  133,08  25,29  158,37  sell);
101 my @testline5  = qw( 0  100,00   83,00   0,00   83,00  sell);  # exchangerate of 0,83
102 my @testline6  = qw(19   56,53   47,50   9,03   56,53   buy);
103 my @testline7  = qw(19  309,86  309,86  58,87  368,73   buy);
104 my @testline8  = qw( 7  130,00  121,50   8,50  130,00   buy);
105 my @testline9  = qw( 7  121,49  121,49   8,50  129,99   buy);
106 my @testline10 = qw( 7  121,50  121,50   8,51  130,01   buy);
107 my @testline11 = qw(19   -2,77   -2,77  -0,53   -3,30   buy);
108 my @testline12 = qw( 7   12,88   12,88   0,90   13,78   buy);
109 my @testline13 = qw(19   41,93   41,93   7,97   49,90   buy);
110 my @testline14 = qw(19   84,65   84,65  16,08  107,73   buy);
111 my @testline15 = qw(19    8,39    8,39   1,59    9,98   buy);
112 my @testline16 = qw(19  100,73   84,65  16,08  107,73   buy);
113 my @testline17 = qw(19    9,99    8,39   1,60    9,99   buy);
114
115 # create testcases, made up of one or more lines, with expected values
116
117 my $testcase1 = {
118     lines           => [ \@testline1 ], # lines to be used in testcase
119     total_amount    => '56,53',  # expected result
120     total_netamount => '47,50',  # expected result
121     total_taxamount => '9,03',   # expected result
122     # invoice parameters:
123     taxincluded     => 1,
124     exchangerate    => 1,
125     currency        => 'EUR',
126     buysell         => 'sell',
127 };
128
129 my $testcase2 = {
130     lines           => [ \@testline1, \@testline2, \@testline3 ],
131     total_amount    => '82,82',
132     total_netamount => '70,95',
133     total_taxamount => '11,87',
134     taxincluded     => 1,
135     exchangerate    => 1,
136     currency        => 'EUR',
137     buysell         => 'sell',
138 };
139
140 my $testcase3 = {
141     lines           => [ \@testline4 ],
142     total_amount    => '158,37',
143     total_netamount => '133,08',
144     total_taxamount => '25,29',
145     taxincluded     => 0,
146     exchangerate    => 1,
147     currency        => 'EUR',
148     buysell         => 'sell',
149 };
150
151 my $testcase4 = {
152     lines           => [ \@testline5 ],
153     total_amount    => '83,00',
154     total_netamount => '83,00',
155     total_taxamount => '0,00',
156     taxincluded     => 0,
157     exchangerate    => '0,83',
158     currency        => 'USD',
159     buysell         => 'sell',
160 };
161
162 my $testcase6 = {
163     lines           => [ \@testline6 ],
164     total_amount    => '56,53',
165     total_netamount => '47,50',
166     total_taxamount => '9,03',
167     taxincluded     => 1,
168     exchangerate    => 1,
169     currency        => 'EUR',
170     buysell         => 'buy',
171 };
172
173 my $testcase7 = {
174     lines           => [ \@testline7 ],
175     total_netamount => '309,86',
176     total_taxamount => '58,87',
177     total_amount    => '368,73',
178     taxincluded     => 0,
179     exchangerate    => 1,
180     currency        => 'EUR',
181     buysell         => 'buy',
182 };
183
184 my $testcase8 = {
185     lines           => [ \@testline8 ],
186     total_netamount => '121,50',
187     total_taxamount => '8,50',
188     total_amount    => '130,00',
189     taxincluded     => 1,
190     exchangerate    => 1,
191     currency        => 'EUR',
192     buysell         => 'buy',
193 };
194
195 my $testcase9 = {
196     lines           => [ \@testline9 ],
197     total_netamount => '121,49',
198     total_taxamount => '8,50',
199     total_amount    => '129,99',
200     taxincluded     => 0,
201     exchangerate    => 1,
202     currency        => 'EUR',
203     buysell         => 'buy',
204 };
205
206 my $testcase10 = {
207     lines           => [ \@testline10 ],
208     total_netamount => '121,50',
209     total_taxamount => '8,51',
210     total_amount    => '130,01',
211     taxincluded     => 0,
212     exchangerate    => 1,
213     currency        => 'EUR',
214     buysell         => 'buy',
215 };
216
217 my $testcase11 = {
218     # mixed invoices, -2,77€ net with 19% as credit note, 12,88€ net with 7%
219     lines           => [ \@testline11 , \@testline12 ],
220     total_netamount => '10,11',
221     total_taxamount => '0,37',
222     total_amount    => '10,48',
223     taxincluded     => 0,
224     exchangerate    => 1,
225     currency        => 'EUR',
226     buysell         => 'buy',
227 };
228
229 my $testcase12 = {
230     # ap transaction, example from bug 2435
231     lines           => [ \@testline13 ],
232     total_netamount => '41,93',
233     total_taxamount => '7,97',
234     total_amount    => '49,90',
235     taxincluded     => 0,
236     exchangerate    => 1,
237     currency        => 'EUR',
238     buysell         => 'buy',
239 };
240
241 my $testcase13 = {
242     # ap transaction, example from bug 2094, tax not included
243     lines           => [ \@testline14 , \@testline15 ],
244     total_netamount => '93,04',
245     total_taxamount => '17,67',
246     total_amount    => '110,71',
247     taxincluded     => 0,
248     exchangerate    => 1,
249     currency        => 'EUR',
250     buysell         => 'buy',
251 };
252
253 my $testcase14 = {
254     # ap transaction, example from bug 2094, tax included
255     lines           => [ \@testline16 , \@testline17 ],
256     total_netamount => '93,04',
257     total_taxamount => '17,68',
258     total_amount    => '110,72',
259     taxincluded     => 1,
260     exchangerate    => 1,
261     currency        => 'EUR',
262     buysell         => 'buy',
263 };
264
265 # run tests
266 arap_test($testcase1);
267 arap_test($testcase2);
268 arap_test($testcase3);
269 arap_test($testcase4);
270 arap_test($testcase6);
271 arap_test($testcase7);
272 arap_test($testcase8);
273 arap_test($testcase9);
274 arap_test($testcase10);
275 arap_test($testcase11);
276 arap_test($testcase12);
277 arap_test($testcase13);
278 arap_test($testcase14);
279
280 # tests for calculate_tax:
281
282 # tests for 1 Cent, calculated tax should be 0
283 calculate_tax_test(0.01,0.07,1,0.01,0.00,0.01,2);
284 calculate_tax_test(0.01,0.19,1,0.01,0.00,0.01,2);
285
286 # tax for rate 7% taxincluded flips at 0.08
287 calculate_tax_test(0.07,0.07,1,0.07,0.00,0.07,2);
288 calculate_tax_test(0.08,0.07,1,0.07,0.01,0.08,2);
289
290 # tax for rate 7% taxexcluded flips at 0.08
291 calculate_tax_test(0.07,0.07,0,0.07,0.00,0.07,2);
292 calculate_tax_test(0.08,0.07,0,0.08,0.01,0.09,2);
293
294 # tax for rate 19% taxexcluded flips at 0.03
295 calculate_tax_test(0.02,0.19,0,0.02,0.00,0.02,2);
296 calculate_tax_test(0.03,0.19,0,0.03,0.01,0.04,2);
297
298 # tax for rate 19% taxincluded flips at 0.04
299 calculate_tax_test(0.03,0.19,1,0.03,0.00,0.03,2);
300 calculate_tax_test(0.04,0.19,1,0.03,0.01,0.04,2);
301
302 calculate_tax_test(8.39,0.19,0,8.39,1.59,9.98,2);
303 calculate_tax_test(9.99,0.19,1,8.39,1.60,9.99,2);
304
305 calculate_tax_test(11.21,0.07,0,11.21,0.78,11.99,2);
306 calculate_tax_test(11.22,0.07,0,11.22,0.79,12.01,2);
307 calculate_tax_test(12.00,0.07,1,11.21,0.79,12.00,2);
308
309 done_testing(82);
310
311 1;