DATEV: Testanpassungen
[kivitendo-erp.git] / t / datev / invoices.t
1 use strict;
2 use Test::More;
3 use Test::Deep qw(cmp_deeply cmp_bag);
4
5 use lib 't';
6 use utf8;
7
8 use_ok 'Support::TestSetup';
9 use SL::DATEV qw(:CONSTANTS);
10 use SL::Dev::ALL qw(:ALL);
11 use List::Util qw(sum);
12 use SL::DB::Buchungsgruppe;
13 use SL::DB::Chart;
14 use DateTime;
15
16 Support::TestSetup::login();
17
18 clear_up();
19
20 my $dbh = SL::DB->client->dbh;
21
22 my $buchungsgruppe7 = SL::DB::Manager::Buchungsgruppe->find_by(description => 'Standard 7%') || die "No accounting group for 7\%";
23 my $bank            = SL::DB::Manager::Chart->find_by(description => 'Bank')                 || die 'Can\'t find chart "Bank"';
24 my $date            = DateTime->new(year => 2017, month =>  1, day => 1);
25 my $payment_date    = DateTime->new(year => 2017, month =>  1, day => 5);
26 my $gldate          = DateTime->new(year => 2017, month =>  2, day => 9); # simulate bookings for Jan being made in Feb
27 my $department      = create_department(description => 'Kostenstelle DATEV-Schnittstelle 2018');
28 my $project         = create_project(projectnumber => 2017, description => 'Crowd-Funding September 2017');
29 my $customer        = new_customer(customernumber => '10001', name => 'Testcustomer')->save;
30 my $vendor          = new_vendor(vendornumber => '70001', name => 'Testvendor')->save;
31
32 my $part1 = new_part(partnumber => '19', description => 'Part 19%')->save;
33 my $part2 = new_part(
34   partnumber         => '7',
35   description        => 'Part 7%',
36   buchungsgruppen_id => $buchungsgruppe7->id,
37 )->save;
38
39 my $invoice = create_sales_invoice(
40   invnumber    => "Þ sales ¥& invöice",
41   customer     => $customer,
42   itime        => $gldate,
43   gldate       => $gldate,
44   intnotes     => 'booked in February',
45   taxincluded  => 0,
46   transdate    => $date,
47   invoiceitems => [ create_invoice_item(part => $part1, qty =>  3, sellprice => 70),
48                     create_invoice_item(part => $part2, qty => 10, sellprice => 50),
49                   ],
50   department_id    => $department->id,
51   globalproject_id => $project->id,
52 );
53 $invoice->pay_invoice(chart_id      => $bank->id,
54                       amount        => $invoice->open_amount,
55                       transdate     => $payment_date->to_kivitendo,
56                       memo          => 'foobar',
57                       source        => 'barfoo',
58                      );
59 my $datev1 = SL::DATEV->new(
60   dbh        => $invoice->db->dbh,
61   trans_id   => $invoice->id,
62 );
63
64 $datev1->generate_datev_data;
65
66 my @data_datev   = sort { $a->{umsatz} <=> $b->{umsatz} } @{ $datev1->generate_datev_lines() };
67 cmp_deeply \@data_datev, [
68                                          {
69                                            'belegfeld1'   => "\x{de} sales \x{a5}& inv\x{f6}ice",
70                                            'buchungstext' => 'Testcustomer',
71                                            'datum'        => '01.01.2017',
72                                            'gegenkonto'   => '8400',
73                                            'konto'        => '1400',
74                                            'kost1'        => 'Kostenstelle DATEV-Schnittstelle 2018',
75                                            'kost2'        => 'Crowd-Funding September 2017',
76                                            'umsatz'       => '249.9',
77                                            'waehrung'     => 'EUR',
78                                          },
79                                          {
80                                            'belegfeld1'   => "\x{de} sales \x{a5}& inv\x{f6}ice",
81                                            'buchungstext' => 'Testcustomer',
82                                            'datum'        => '01.01.2017',
83                                            'gegenkonto'   => '8300',
84                                            'konto'        => '1400',
85                                            'kost1'        => 'Kostenstelle DATEV-Schnittstelle 2018',
86                                            'kost2'        => 'Crowd-Funding September 2017',
87                                            'umsatz'       => 535,
88                                            'waehrung'     => 'EUR',
89                                          },
90                                          {
91                                            'belegfeld1'   => "\x{de} sales \x{a5}& inv\x{f6}ice",
92
93
94 'buchungstext' => 'Testcustomer',
95                                            'buchungstext' => 'Testcustomer',
96                                            'datum'        => '05.01.2017',
97                                            'gegenkonto'   => '1400',
98                                            'konto'        => '1200',
99                                            'kost1'        => 'Kostenstelle DATEV-Schnittstelle 2018',
100                                            'kost2'        => 'Crowd-Funding September 2017',
101                                            'umsatz'       => '784.9',
102                                            'waehrung'     => 'EUR',
103                                          },
104                                        ], "trans_id datev check ok";
105
106 $datev1->use_pk(1);
107 $datev1->generate_datev_data;
108 # TODO for cmp_deeply we need to sort the incoming data structure (see below)
109 cmp_bag $datev1->generate_datev_lines, [
110                                          {
111                                            'belegfeld1'   => "\x{de} sales \x{a5}& inv\x{f6}ice",
112                                            'buchungstext' => 'Testcustomer',
113                                            'datum'        => '01.01.2017',
114                                            'gegenkonto'   => '8400',
115                                            'konto'        => $customer->customernumber,
116                                            'kost1'        => 'Kostenstelle DATEV-Schnittstelle 2018',
117                                            'kost2'        => 'Crowd-Funding September 2017',
118                                            'umsatz'       => '249.9',
119                                            'waehrung'     => 'EUR',
120                                          },
121                                          {
122                                            'belegfeld1'   => "\x{de} sales \x{a5}& inv\x{f6}ice",
123                                            'buchungstext' => 'Testcustomer',
124                                            'datum'        => '01.01.2017',
125                                            'gegenkonto'   => '8300',
126                                            'konto'        => $customer->customernumber,
127                                            'kost1'        => 'Kostenstelle DATEV-Schnittstelle 2018',
128                                            'kost2'        => 'Crowd-Funding September 2017',
129                                            'umsatz'       => 535,
130                                            'waehrung'     => 'EUR',
131                                          },
132                                          {
133                                            'belegfeld1'   => "\x{de} sales \x{a5}& inv\x{f6}ice",
134                                            'buchungstext' => 'Testcustomer',
135                                            'datum'        => '05.01.2017',
136                                            'gegenkonto'   => $customer->customernumber,
137                                            'konto'        => '1200',
138                                            'kost1'        => 'Kostenstelle DATEV-Schnittstelle 2018',
139                                            'kost2'        => 'Crowd-Funding September 2017',
140                                            'umsatz'       => '784.9',
141                                            'waehrung'     => 'EUR',
142                                          },
143                                        ], "trans_id datev check use_pk ok";
144
145
146 my $startdate = DateTime->new(year => 2017, month =>  1, day =>  1);
147 my $enddate   = DateTime->new(year => 2017, month => 12, day => 31);
148
149 # check conversion to csv
150 $datev1->from($startdate);
151 $datev1->to($enddate);
152 # reset use_pk for csv_buchungsexport
153 $datev1->use_pk(0);
154 $datev1->generate_datev_data;
155
156
157 my $datev_csv = SL::DATEV::CSV->new(datev_lines  => $datev1->generate_datev_lines,
158                                     from         => $startdate,
159                                     to           => $enddate,
160                                     locked       => $datev1->locked,
161                                    );
162 $datev_csv->lines;
163
164
165 # we need sort, because pay_invoice is not acc_trans_id order safe
166 my @data_csv    = sort { $a->[0] cmp $b->[0] } @{ $datev_csv->lines };
167 # warnings should be undef -> no array elements at all
168 is(scalar @{ $datev_csv->warnings }, 0);
169
170
171 cmp_deeply($data_csv[1], [ '535', 'S', 'EUR', '', '', '', '1400', '8300', '', '0101', "\x{de} sales \x{a5}& i",
172                      '', '', 'Testcustomer', '', '', '', '', '', '', '', '',
173                      '', '', '', '', '', '', '', '', '', '', '', '', '',
174                      '', 'Kostenst', 'Crowd-Fu', '', '', '', '', '', '', '', '',
175                      '', '', '', '', '', '', '', '', '', '', '', '', '',
176                      '', '', '', '', '', '', '', '', '', '', '', '', '',
177                      '', '', '', '', '', '', '', '', '', '', '', '', '',
178                      '', '', '', '', '', '', '', '', '', '', '', '', '',
179                      '', '', '', '', '', '', '', '', '', '', '', '', '',
180                      '', '', '', '', '' ]
181        );
182
183 cmp_deeply($data_csv[0], [ '249,9', 'S', 'EUR', '', '', '', '1400', '8400', '', '0101', "\x{de} sales \x{a5}& i",
184                      '', '', 'Testcustomer', '', '', '', '', '', '', '', '',
185                      '', '', '', '', '', '', '', '', '', '', '', '', '',
186                      '', 'Kostenst', 'Crowd-Fu', '', '', '', '', '', '', '', '',
187                      '', '', '', '', '', '', '', '', '', '', '', '', '',
188                      '', '', '', '', '', '', '', '', '', '', '', '', '',
189                      '', '', '', '', '', '', '', '', '', '', '', '', '',
190                      '', '', '', '', '', '', '', '', '', '', '', '', '',
191                      '', '', '', '', '', '', '', '', '', '', '', '', '',
192                      '', '', '', '', '' ]
193        );
194 cmp_deeply($data_csv[2], [ '784,9', 'S', 'EUR', '', '', '', '1200', '1400', '', '0501', "\x{de} sales \x{a5}& i",
195                      '', '', 'Testcustomer', '', '', '', '', '', '', '', '',
196                      '', '', '', '', '', '', '', '', '', '', '', '', '',
197                      '', 'Kostenst', 'Crowd-Fu', '', '', '', '', '', '', '', '',
198                      '', '', '', '', '', '', '', '', '', '', '', '', '',
199                      '', '', '', '', '', '', '', '', '', '', '', '', '',
200                      '', '', '', '', '', '', '', '', '', '', '', '', '',
201                      '', '', '', '', '', '', '', '', '', '', '', '', '',
202                      '', '', '', '', '', '', '', '', '', '', '', '', '',
203                      '', '', '', '', '' ]
204         );
205 my $march_9 = DateTime->new(year => 2017, month =>  3, day => 9);
206 my $invoice2 = create_sales_invoice(
207   invnumber    => "2 sales invoice",
208   customer     => $customer,
209   itime        => $march_9,
210   gldate       => $march_9,
211   intnotes     => 'booked in March',
212   taxincluded  => 0,
213   transdate    => $date,
214   invoiceitems => [ create_invoice_item(part => $part1, qty =>  6, sellprice => 70),
215                     create_invoice_item(part => $part2, qty => 20, sellprice => 50),
216                   ]
217 );
218
219 my $credit_note = create_credit_note(
220   invnumber    => 'Gutschrift 34',
221   customer     => $customer,
222   itime        => $gldate,
223   gldate       => $gldate,
224   intnotes     => 'booked in February',
225   taxincluded  => 0,
226   transdate    => $date,
227   invoiceitems => [ create_invoice_item(part => $part1, qty =>  3, sellprice => 70),
228                     create_invoice_item(part => $part2, qty => 10, sellprice => 50),
229                   ]
230 );
231
232 my $datev = SL::DATEV->new(
233   dbh        => $dbh,
234   from       => $startdate,
235   to         => $enddate,
236 );
237 $datev->generate_datev_data(from_to => $datev->fromto);
238 my $datev_lines = $datev->generate_datev_lines;
239 my $umsatzsumme = sum map { $_->{umsatz} } @{ $datev_lines };
240 cmp_ok($::form->round_amount($umsatzsumme,2), '==', 3924.5, "Sum of all bookings ok");
241
242 $datev->generate_datev_data(use_pk => 1, from_to => $datev->fromto);
243 $datev_lines = $datev->generate_datev_lines;
244
245 note('testing purchase invoice');
246 my $purchase_invoice = new_purchase_invoice();
247 $datev1 = SL::DATEV->new(
248   dbh        => $purchase_invoice->db->dbh,
249   trans_id   => $purchase_invoice->id,
250 );
251
252 $datev1->generate_datev_data;
253 cmp_deeply $datev1->generate_datev_lines, [
254                                         {
255                                           'belegfeld1'             => 'ap1',
256                                           'buchungstext'           => 'Testvendor',
257                                           'datum'                  => '01.01.2017',
258                                           'gegenkonto'             => '1600',
259                                           'konto'                  => '3400',
260                                           'kost1'                  => undef,
261                                           'kost2'                  => undef,
262                                           'umsatz'                 => 119,
263                                           'waehrung'               => 'EUR'
264                                         },
265                                         {
266                                           'belegfeld1'             => 'ap1',
267                                           'buchungstext'           => 'Testvendor',
268                                           'datum'                  => '01.01.2017',
269                                           'gegenkonto'             => '1600',
270                                           'konto'                  => '3300',
271                                           'kost1'                  => undef,
272                                           'kost2'                  => undef,
273                                           'umsatz'                 => 107,
274                                           'waehrung'               => 'EUR'
275                                         }
276                                        ], "trans_id datev check purchase_invoice ok";
277 $datev1->use_pk(1);
278 $datev1->generate_datev_data;
279 cmp_deeply $datev1->generate_datev_lines, [
280                                         {
281                                           'belegfeld1'             => 'ap1',
282                                           'buchungstext'           => 'Testvendor',
283                                           'datum'                  => '01.01.2017',
284                                           'gegenkonto'             => $vendor->vendornumber,
285                                           'konto'                  => '3400',
286                                           'kost1'                  => undef,
287                                           'kost2'                  => undef,
288                                           'umsatz'                 => 119,
289                                           'waehrung'               => 'EUR'
290                                         },
291                                         {
292                                           'belegfeld1'             => 'ap1',
293                                           'buchungstext'           => 'Testvendor',
294                                           'datum'                  => '01.01.2017',
295                                           'gegenkonto'             => $vendor->vendornumber,
296                                           'konto'                  => '3300',
297                                           'kost1'                  => undef,
298                                           'kost2'                  => undef,
299                                           'umsatz'                 => 107,
300                                           'waehrung'               => 'EUR'
301                                         }
302                                        ], "trans_id datev check purchase_invoice use_pk ok";
303
304 note('testing gldatefrom');
305 $datev = SL::DATEV->new(
306   dbh        => $dbh,
307   from       => $startdate,
308   to         => DateTime->new(year => 2017, month => 01, day => 31),
309 );
310
311 $::form               = Support::TestSetup->create_new_form;
312 $::form->{gldatefrom} = DateTime->new(year => 2017, month => 3, day => 1)->to_kivitendo;
313
314 $datev->generate_datev_data(from_to => $datev->fromto);
315 $datev_lines = $datev->generate_datev_lines;
316 $umsatzsumme = sum map { $_->{umsatz} } @{ $datev_lines };
317 cmp_ok($umsatzsumme, '==', 1569.8, "Sum of bookings made after March 1st (only invoice2) ok");
318
319 $::form->{gldatefrom} = DateTime->new(year => 2017, month => 5, day => 1)->to_kivitendo;
320 $datev->generate_datev_data(from_to => $datev->fromto);
321 cmp_deeply $datev->generate_datev_lines, [], "no bookings for January made after May 1st: ok";
322
323 done_testing();
324 # clear_up();
325
326 sub new_purchase_invoice {
327   # manually create a Kreditorenbuchung from scratch, ap + acc_trans bookings, as no helper exists yet, like $invoice->post.
328   # arap-Booking must come last in the acc_trans order
329   # this function was essentially copied from t/db_helper/payment.t, refactor once $purchase_invoice->post exists
330   my $currency_id = $::instance_conf->get_currency_id;
331   my $employee    = SL::DB::Manager::Employee->current                          || die "No employee";
332   my $taxzone     = SL::DB::Manager::TaxZone->find_by( description => 'Inland') || die "No taxzone";
333
334   my $purchase_invoice = SL::DB::PurchaseInvoice->new(
335     amount      => '226',
336     currency_id => $currency_id,
337     employee_id => $employee->id,
338     gldate      => $date,
339     invnumber   => "ap1",
340     invoice     => 0,
341     itime       => $date,
342     mtime       => $date,
343     netamount   => '200',
344     paid        => '0',
345     taxincluded => 0,
346     taxzone_id  => $taxzone->id,
347     transdate   => $date,
348     type        => 'invoice',
349     vendor_id   => $vendor->id,
350   )->save;
351
352   my $expense_chart  = SL::DB::Manager::Chart->find_by(accno => '3400');
353   my $expense_chart_booking= SL::DB::AccTransaction->new(
354     amount     => '-100',
355     chart_id   => $expense_chart->id,
356     chart_link => $expense_chart->link,
357     itime      => $date,
358     mtime      => $date,
359     source     => '',
360     tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 9)->id,
361     taxkey     => 9,
362     transdate  => $date,
363     trans_id   => $purchase_invoice->id,
364   );
365   $expense_chart_booking->save;
366
367   my $tax_chart  = SL::DB::Manager::Chart->find_by(accno => '1576');
368   my $tax_chart_booking= SL::DB::AccTransaction->new(
369     amount     => '-19',
370     chart_id   => $tax_chart->id,
371     chart_link => $tax_chart->link,
372     itime      => $date,
373     mtime      => $date,
374     source     => '',
375     tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 9)->id,
376     taxkey     => 0,
377     transdate  => $date,
378     trans_id   => $purchase_invoice->id,
379   );
380   $tax_chart_booking->save;
381   $expense_chart  = SL::DB::Manager::Chart->find_by(accno => '3300');
382   $expense_chart_booking= SL::DB::AccTransaction->new(
383     amount     => '-100',
384     chart_id   => $expense_chart->id,
385     chart_link => $expense_chart->link,
386     itime      => $date,
387     mtime      => $date,
388     source     => '',
389     tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 8)->id,
390     taxkey     => 8,
391     transdate  => $date,
392     trans_id   => $purchase_invoice->id,
393   );
394   $expense_chart_booking->save;
395
396   $tax_chart  = SL::DB::Manager::Chart->find_by(accno => '1571');
397   $tax_chart_booking= SL::DB::AccTransaction->new(
398     trans_id   => $purchase_invoice->id,
399     chart_id   => $tax_chart->id,
400     chart_link => $tax_chart->link,
401     amount     => '-7',
402     transdate  => $date,
403     itime      => $date,
404     mtime      => $date,
405     source     => '',
406     taxkey     => 0,
407     tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 8)->id,
408   );
409   $tax_chart_booking->save;
410   my $arap_chart  = SL::DB::Manager::Chart->find_by(accno => '1600');
411   my $arap_booking= SL::DB::AccTransaction->new(
412     trans_id   => $purchase_invoice->id,
413     chart_id   => $arap_chart->id,
414     chart_link => $arap_chart->link,
415     amount     => '226',
416     transdate  => $date,
417     itime      => $date,
418     mtime      => $date,
419     source     => '',
420     taxkey     => 0,
421     tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 0)->id,
422   );
423   $arap_booking->save;
424
425   return $purchase_invoice;
426 }
427
428 sub clear_up {
429   SL::DB::Manager::AccTransaction->delete_all(all => 1);
430   SL::DB::Manager::InvoiceItem->delete_all(   all => 1);
431   SL::DB::Manager::Invoice->delete_all(       all => 1);
432   SL::DB::Manager::PurchaseInvoice->delete_all(all => 1);
433   SL::DB::Manager::Customer->delete_all(      all => 1);
434   SL::DB::Manager::Part->delete_all(          all => 1);
435   SL::DB::Manager::Project->delete_all(       all => 1);
436   SL::DB::Manager::Department->delete_all(    all => 1);
437   SL::DATEV->clean_temporary_directories;
438 };
439
440 1;