Consolidation and extended test runs
[kivitendo-erp.git] / t / db_helper / convert_invoice.t
1 use Test::More tests => 42;
2
3 use strict;
4
5 use lib 't';
6 use utf8;
7
8 use Support::TestSetup;
9
10 use Carp;
11 use Data::Dumper;
12 use Support::TestSetup;
13 use Test::Exception;
14 use List::Util qw(max);
15
16 use SL::DB::Buchungsgruppe;
17 use SL::DB::Currency;
18 use SL::DB::Customer;
19 use SL::DB::Employee;
20 use SL::DB::Invoice;
21 use SL::DB::Order;
22 use SL::DB::DeliveryOrder;
23 use SL::DB::Part;
24 use SL::DB::Unit;
25 use SL::DB::TaxZone;
26
27 my ($customer, $currency_id, $buchungsgruppe, $employee, $vendor, $taxzone, $buchungsgruppe7, $tax, $tax7,
28     $unit, @parts);
29
30 my $VISUAL_TEST = 0;  # just a sleep to click around
31
32 sub clear_up {
33   foreach (qw(DeliveryOrderItem DeliveryOrder InvoiceItem Invoice Part Customer Vendor Department PaymentTerm)) {
34     "SL::DB::Manager::${_}"->delete_all(all => 1);
35   }
36   SL::DB::Manager::Employee->delete_all(where => [ id => 31915 ]);
37 };
38
39 sub reset_state {
40   my %params = @_;
41
42   clear_up();
43
44   if ($::lx_office_conf{system}->{default_manager} eq "swiss") {
45     $buchungsgruppe  = SL::DB::Manager::Buchungsgruppe->find_by(description => 'Standard 8%', %{ $params{buchungsgruppe} })  || croak "No accounting group 8\%";
46     $buchungsgruppe7 = SL::DB::Manager::Buchungsgruppe->find_by(description => 'Standard 2.5%', %{ $params{buchungsgruppe} })|| croak "No accounting group 2.5\%";
47     $taxzone         = SL::DB::Manager::TaxZone->find_by( description => 'Schweiz')                                          || croak "No taxzone";
48     $tax             = SL::DB::Manager::Tax->find_by(taxkey => 2, rate => 0.08, %{ $params{tax} })                           || croak "No tax for 8\%";
49     $tax7            = SL::DB::Manager::Tax->find_by(taxkey => 3, rate => 0.025)                                             || croak "No tax for 2.5\%";
50     $unit            = SL::DB::Manager::Unit->find_by(name => 'kg', %{ $params{unit} })                                      || croak "No unit";
51     $currency_id     = $::instance_conf->get_currency_id;
52   } else {
53     $buchungsgruppe  = SL::DB::Manager::Buchungsgruppe->find_by(description => 'Standard 19%', %{ $params{buchungsgruppe} }) || croak "No accounting group 19\%";
54     $buchungsgruppe7 = SL::DB::Manager::Buchungsgruppe->find_by(description => 'Standard 7%', %{ $params{buchungsgruppe} })  || croak "No accounting group 7\%";
55     $taxzone         = SL::DB::Manager::TaxZone->find_by( description => 'Inland')                                           || croak "No taxzone";
56     $tax             = SL::DB::Manager::Tax->find_by(taxkey => 3, rate => 0.19, %{ $params{tax} })                           || croak "No tax for 19\%";
57     $tax7            = SL::DB::Manager::Tax->find_by(taxkey => 2, rate => 0.07)                                              || croak "No tax for 7\%";
58     $unit            = SL::DB::Manager::Unit->find_by(name => 'kg', %{ $params{unit} })                                      || croak "No unit";
59     $currency_id     = $::instance_conf->get_currency_id;
60   }
61
62   $customer     = SL::DB::Customer->new(
63     name        => '520484567dfaedc9e60fc',
64     currency_id => $currency_id,
65     taxzone_id  => $taxzone->id,
66     %{ $params{customer} }
67   )->save;
68
69   # some od.rnr real anonym data
70   my $employee_bk = SL::DB::Employee->new(
71                 'id' => 31915,
72                 'login' => 'barbuschka.kappes',
73                 'name' => 'Barbuschka Kappes',
74   )->save;
75
76   my $department_do = SL::DB::Department->new(
77                  'description' => 'Maisenhaus-Versand',
78                  'id' => 32149,
79                  'itime' => undef,
80                  'mtime' => undef
81   )->save;
82
83   my $payment_do = SL::DB::PaymentTerm->new(
84                  'description' => '14Tage 2%Skonto, 30Tage netto',
85                  'description_long' => "Innerhalb von 14 Tagen abzüglich 2 % Skonto, innerhalb von 30 Tagen rein netto.|Bei einer Zahlung bis zum <%skonto_date%> gewähren wir 2 % Skonto (EUR <%skonto_amount%>) entspricht EUR <%total_wo_skonto%>.Bei einer Zahlung bis zum <%netto_date%> ist der fällige Betrag in Höhe von <%total%> <%currency%> zu überweisen.",
86                  'id' => 11276,
87                  'itime' => undef,
88                  'mtime' => undef,
89                  'percent_skonto' => '0.02',
90                  'ranking' => undef,
91                  'sortkey' => 4,
92                  'terms_netto' => 30,
93                  'auto_calculation' => undef,
94                  'terms_skonto' => 14
95   )->save;
96
97   # two real parts
98   @parts = ();
99   push @parts, SL::DB::Part->new(
100                  'id' => 26321,
101                  'image' => '',
102                  'lastcost' => '49.95000',
103                  'listprice' => '0.00000',
104                  'onhand' => '5.00000',
105                  'partnumber' => 'v-519160549',
106                  #'partsgroup_id' => 111645,
107                  'rop' => '0',
108                  'sellprice' => '242.20000',
109                  #'warehouse_id' => 64702,
110                  'weight' => '0.79',
111                  description        => "Pflaumenbaum, Gr.5, Unterfilz weinrot, genietet[[Aufschnittbreite: 11,0, Kernform: US]]\"" ,
112                  buchungsgruppen_id => $buchungsgruppe->id,
113                  unit               => $unit->name,
114                  id                 => 26321,
115   )->save;
116
117   push @parts, SL::DB::Part->new(
118                  'description' => "[[0640]]Flügel Hammerstiele bestehend aus:
119 70 Stielen Standard in Weißbuche und
120 20 Stielen Diskant abgekehlt in Weißbuche
121 mit Röllchen aus Synthetikleder,
122 Kapseln mit Yamaha Profil, Kerbenabstand 3,6 mm mit eingedrehten Abnickschrauben",
123                  'id' => 25505,
124                  'lastcost' => '153.00000',
125                  'listprice' => '0.00000',
126                  'onhand' => '9.00000',
127                  'partnumber' => 'v-120160086',
128                  # 'partsgroup_id' => 111639,
129                  'rop' => '0',
130                  'sellprice' => '344.30000',
131                  'weight' => '0.9',
132                   buchungsgruppen_id => $buchungsgruppe->id,
133                   unit               => $unit->name,
134   )->save;
135 }
136
137 sub new_delivery_order {
138   my %params  = @_;
139
140   return SL::DB::DeliveryOrder->new(
141    currency_id => $currency_id,
142    taxzone_id  => $taxzone->id,
143     %params,
144   )->save;
145 }
146
147 Support::TestSetup::login();
148
149 reset_state();
150
151 # we create L20199 with two items
152 my $do1 = new_delivery_order('department_id'    => 32149,
153                              'donumber'         => 'L20199',
154                              'employee_id'      => 31915,
155                              'intnotes'         => 'Achtung: Neue Lieferadresse ab 16.02.2015 in der Otto-Merck-Str. 7a!   13.02.2015/MH
156
157                                             Yamaha-Produkte (201...) immer plus 25% dazu rechnen / BK 13.02.2014',
158                               'ordnumber'       => 'A16399',
159                               'payment_id'      => 11276,
160                               'salesman_id'     => 31915,
161                               'shippingpoint'   => 'Maisenhaus',
162                               # 'shipto_id'     => 451463,
163                               'is_sales'        => 'true',
164                               'shipvia'         => 'DHL, Versand am 06.03.2015, 1 Paket  17,00 kg',
165                               'taxzone_id'      => 4,
166                               'closed'          => undef,
167                               # 'currency_id'   => 1,
168                               'cusordnumber'    => 'b84da',
169                               'customer_id'     => $customer->id,
170                               'id'              => 464003,
171                               'notes'           => '<ul><li><strong>fett</strong></li><li><strong>und</strong></li><li><strong>mit</strong></li><li><strong>bullets</strong></li><li>&nbsp;</li></ul>',
172 );
173
174 my $do1_item1 = SL::DB::DeliveryOrderItem->new('delivery_order_id' => 464003,
175                                                'description' => "Flügel Hammerkopf bestehend aus:
176                                                                  Bass/Diskant 26/65 Stück, Gesamtlänge 80/72, Bohrlänge 56/48
177                                                                  Pflaumenbaum, Gr.5, Unterfilz weinrot, genietet[[Aufschnittbreite: 11,0, Kernform: US]]",
178                                                'discount' => '0.25',
179                                                'id' => 144736,
180                                                'lastcost' => '49.95000',
181                                                'longdescription'    => "<ol><li>27</li><li>28</li><li>29</li><li><sub>asdf</sub></li><li><sub>asdf</sub></li><li><sup>oben</sup></li></ol><p><s>kommt nicht mehr vor</s></p>",
182                                                'marge_price_factor' => 1,
183                                                'mtime' => undef,
184                                                'ordnumber' => 'A16399',
185                                                'parts_id' => 26321,
186                                                'position' => 1,
187                                                'price_factor' => 1,
188                                                'qty' => '2.00000',
189                                                'sellprice' => '242.20000',
190                                                'transdate' => '06.03.2015',
191                                                'unit' => 'kg')->save;
192
193 my $do1_item2 = SL::DB::DeliveryOrderItem->new('delivery_order_id' => 464003,
194                  'description' => "[[0640]]Flügel Hammerstiele bestehend aus:
195 70 Stielen Standard in Weißbuche und
196 20 Stielen Diskant abgekehlt in Weißbuche
197 mit Röllchen aus Synthetikleder,
198 Kapseln mit Yamaha Profil, Kerbenabstand 3,6 mm mit eingedrehten Abnickschrauben",
199                  'discount' => '0.25',
200                  'id' => 144737,
201                  'itime' => undef,
202                  'lastcost' => '153.00000',
203                  'longdescription' => '',
204                  'marge_price_factor' => 1,
205                  'mtime' => undef,
206                  'ordnumber' => 'A16399',
207                  'parts_id' => 25505,
208                  'position' => 2,
209                  'price_factor' => 1,
210                  'price_factor_id' => undef,
211                  'pricegroup_id' => undef,
212                  'project_id' => undef,
213                  'qty' => '3.00000',
214                  'reqdate' => undef,
215                  'sellprice' => '344.30000',
216                  'serialnumber' => '',
217                  'transdate' => '06.03.2015',
218                  'unit' => 'kg')->save;
219
220 # TESTS
221
222
223 # test delivery order before any conversion
224 ok($do1->donumber eq "L20199", 'Delivery Order Number created');
225 ok($do1->notes eq '<ul><li><strong>fett</strong></li><li><strong>und</strong></li><li><strong>mit</strong></li><li><strong>bullets</strong></li><li>&nbsp;</li></ul>', "do RichText notes saved");
226 ok((not $do1->closed) , 'Delivery Order is not closed');
227 ok($do1_item1->parts_id eq '26321', 'doi linked with part');
228 ok($do1_item1->qty == 2, 'qty check doi');
229 ok($do1_item1->longdescription eq  "<ol><li>27</li><li>28</li><li>29</li><li><sub>asdf</sub></li><li><sub>asdf</sub></li><li><sup>oben</sup></li></ol><p><s>kommt nicht mehr vor</s></p>",
230      "do item1 rich text longdescripition");
231 ok ($do1_item2->position == 2, 'doi2 position check');
232 ok (2 ==  scalar@{ SL::DB::Manager::DeliveryOrderItem->get_all(where => [ delivery_order_id => $do1->id ]) }, 'two doi linked');
233
234
235 # convert this do to invoice
236 my $invoice = $do1->convert_to_invoice();
237
238 sleep (300) if $VISUAL_TEST; # we can do a real visual test via gui login
239 # test invoice afterwards
240
241 ok ($invoice->shipvia eq "DHL, Versand am 06.03.2015, 1 Paket  17,00 kg", "ship via check");
242 ok ($invoice->shippingpoint eq "Maisenhaus", "shipping point check");
243 ok ($invoice->ordnumber eq "A16399", "ordnumber check");
244 ok ($invoice->donumber eq "L20199", "donumber check");
245 ok ($invoice->notes eq '<ul><li><strong>fett</strong></li><li><strong>und</strong></li><li><strong>mit</strong></li><li><strong>bullets</strong></li><li>&nbsp;</li></ul>', "do RichText notes saved");
246 ok(($do1->closed) , 'Delivery Order is closed after conversion');
247 ok (SL::DB::PaymentTerm->new(id => $invoice->{payment_id})->load->description eq "14Tage 2%Skonto, 30Tage netto", 'payment term description check');
248
249 # some test data from original client invoice console (!)
250 # my $invoice3 = SL::DB::Manager::Invoice->find_by( ordnumber => 'A16399' );
251 # which will fail due to PTC Calculation differs from GUI-Calculation, see issue: http://redmine.kivitendo-premium.de/issues/82
252 # pp $invoice3
253 # values from gui should be:
254 #ok($invoice->amount == 1354.20000, 'amount check');
255 #ok($invoice->marge_percent == 50.88666, 'marge percent check');
256 #ok($invoice->marge_total == 579.08000, 'marge total check');
257 #ok($invoice->netamount == 1137.98000, 'netamount check');
258
259
260 # the values change if one reloads the object
261 # without reloading we get this failures
262 #not ok 17 - amount check
263 #   Failed test 'amount check'
264 #   at t/db_helper/convert_invoice.t line 272.
265 #          got: '1354.17'
266 #     expected: '1354.17000'
267 #not ok 18 - marge percent check
268 #   Failed test 'marge percent check'
269 #   at t/db_helper/convert_invoice.t line 273.
270 #          got: '50.8857956342929'
271 #     expected: '50.88580'
272 #not ok 19 - marge total check
273 #   Failed test 'marge total check'
274 #   at t/db_helper/convert_invoice.t line 274.
275 #          got: '579.06'
276 #     expected: '579.06000'
277 #not ok 20 - netamount check
278 #   Failed test 'netamount check'
279 #   at t/db_helper/convert_invoice.t line 275.
280 #          got: '1137.96'
281 #     expected: '1137.96000'
282
283 $invoice->load;
284
285 ok($invoice->currency_id eq '1', 'currency_id');
286 ok($invoice->cusordnumber eq 'b84da', 'cusordnumber check');
287 ok(SL::DB::Department->new(id => $invoice->{department_id})->load->description eq "Maisenhaus-Versand", 'department description');
288 if ($::lx_office_conf{system}->{default_manager} eq "swiss") {
289   is($invoice->amount, '1229.00000', 'amount check');
290 } else {
291   is($invoice->amount, '1354.17000', 'amount check');
292 }
293 is($invoice->marge_percent, '50.88580', 'marge percent check');
294 is($invoice->marge_total, '579.06000', 'marge total check');
295 is($invoice->netamount, '1137.96000', 'netamount check');
296
297 # some item checks
298 ok(@ {$invoice->items_sorted}[0]->parts_id eq '26321', 'invoiceitem 1 linked with part');
299 ok(2 ==  scalar@{ $invoice->invoiceitems }, 'two invoice items linked with invoice');
300 is(@ {$invoice->items_sorted}[0]->position, 1, "position 1 order correct");
301 is(@ {$invoice->items_sorted}[1]->position, 2, "position 2 order correct");
302 is(@ {$invoice->items_sorted}[0]->longdescription, "<ol><li>27</li><li>28</li><li>29</li><li><sub>asdf</sub></li><li><sub>asdf</sub></li><li><sup>oben</sup></li></ol><p><s>kommt nicht mehr vor</s></p>",
303      "invoice item1 rich text longdescripition");
304 is(@ {$invoice->items_sorted}[0]->part->partnumber, 'v-519160549', "partnumber 1 correct");
305 is(@ {$invoice->items_sorted}[1]->part->partnumber, 'v-120160086', "partnumber 2 correct");
306 is(@ {$invoice->items_sorted}[0]->qty, '2.00000', "pos 1 qty");
307 is(@ {$invoice->items_sorted}[1]->qty, '3.00000', "pos 2 qty");
308 is(@ {$invoice->items_sorted}[0]->discount, 0.25, "pos 1 discount");
309 is(@ {$invoice->items_sorted}[1]->discount, 0.25, "pos 2 discount");
310
311 # more ideas: check onhand, lastcost (parsed lastcost)
312
313
314
315 # check linked records AND linked items
316
317 # we expect something like this in record links:
318 # delivery_order_items |  144736 | invoice  |     9 | 2015-09-02 16:29:32.362562 |  5
319 # delivery_order_items |  144737 | invoice  |    10 | 2015-09-02 16:29:32.362562 |  6
320 # delivery_orders      |  464003 | ar       |     5 | 2015-09-02 16:29:32.362562 |  7
321 # wir erwarten:
322 # verkn�pfter beleg$VAR1 = {
323 #           'from_id' => 464003,
324 #           'from_table' => 'delivery_orders',
325 #           'to_id' => 11,
326 #           'to_table' => 'ar'
327 #         };
328 # verkn�pfte positionen$VAR1 = {
329 #           'from_id' => 144737,
330 #           'from_table' => 'delivery_order_items',
331 #           'to_id' => 22,
332 #           'to_table' => 'invoice'
333 #         };
334 # $VAR2 = {
335 #           'from_id' => 144736,
336 #           'from_table' => 'delivery_order_items',
337 #           'to_id' => 21,
338 #           'to_table' => 'invoice'
339 #         };
340
341
342
343 my @links_record    = RecordLinks->get_links('from_table' => 'delivery_orders',
344                                              'to_table'   => 'ar',
345                                              'from_id'      => 464003);
346 is($links_record[0]->{from_id}, '464003', "record from id check");
347 is($links_record[0]->{from_table}, 'delivery_orders', "record from table check");
348 is($links_record[0]->{to_table}, 'ar', "record to table check");
349
350 foreach (qw(144736 144737)) {
351   my @links_record_item1 = RecordLinks->get_links('from_table' => 'delivery_order_items',
352                                                  'to_table'   => 'invoice',
353                                                  'from_id'      => $_);
354   is($links_record_item1[0]->{from_id}, $_, "record from id check $_");
355   is($links_record_item1[0]->{from_table}, 'delivery_order_items', "record from table check $_");
356   is($links_record_item1[0]->{to_table}, 'invoice', "record to table check $_");
357 }
358
359
360 clear_up();
361
362 1;