Zeiterfassung: Unit-Test: Runden/Nicht runden testen
[kivitendo-erp.git] / t / background_job / convert_time_recordings.t
1 use Test::More tests => 40;
2
3 use strict;
4
5 use lib 't';
6 use utf8;
7
8 use Support::TestSetup;
9 use Test::Exception;
10 use DateTime;
11 use Rose::DB::Object::Helpers qw(forget_related);
12
13 use SL::DB::BackgroundJob;
14 use SL::DB::DeliveryOrder;
15
16 use_ok 'SL::BackgroundJob::ConvertTimeRecordings';
17
18 use SL::Dev::ALL qw(:ALL);
19
20 Support::TestSetup::login();
21
22 sub clear_up {
23   foreach (qw(TimeRecording OrderItem Order DeliveryOrder Project Part Customer RecordLink)) {
24     "SL::DB::Manager::${_}"->delete_all(all => 1);
25   }
26   SL::DB::Manager::Employee->delete_all(where => [ '!login' => 'unittests' ]);
27 };
28
29 ########################################
30
31 $::myconfig{numberformat} = '1000.00';
32 my $old_locale = $::locale;
33 # set locale to en so we can match errors
34 $::locale = Locale->new('en');
35
36
37 clear_up();
38
39 ########################################
40 # two time recordings, one order linked with project_id
41 ########################################
42 my $part     = new_service(partnumber => 'Serv1', unit => 'Std')->save;
43 my $project  = create_project(projectnumber => 'p1', description => 'Project 1');
44 my $customer = new_customer()->save;
45
46 # sales order with globalproject_id
47 my $sales_order = create_sales_order(
48   save             => 1,
49   customer         => $customer,
50   globalproject    => $project,
51   taxincluded      => 0,
52   orderitems       => [ create_order_item(part => $part, qty => 3, sellprice => 70), ]
53 );
54
55 my @time_recordings;
56 push @time_recordings, new_time_recording(
57   start_time => DateTime->new(year => 2021, month =>  4, day => 19, hour => 10, minute =>  5),
58   end_time   => DateTime->new(year => 2021, month =>  4, day => 19, hour => 11, minute =>  5),
59   customer   => $customer,
60   project    => $project,
61   part       => $part,
62 )->save;
63 push @time_recordings, new_time_recording(
64   start_time => DateTime->new(year => 2021, month =>  4, day => 19, hour => 12, minute =>  5),
65   end_time   => DateTime->new(year => 2021, month =>  4, day => 19, hour => 14, minute =>  5),
66   customer   => $customer,
67   project    => $project,
68   part       => $part,
69 )->save;
70
71 my %data   = (
72   link_order => 1,
73   project_id => $project->id,
74   from_date  => '01.01.2021',
75   to_date    => '30.04.2021',
76 );
77 my $db_obj = SL::DB::BackgroundJob->new();
78 $db_obj->set_data(%data);
79 my $job    = SL::BackgroundJob::ConvertTimeRecordings->new;
80 my $ret    = $job->run($db_obj);
81
82 is_deeply($job->{job_errors}, [], 'no errros');
83 like($ret, qr{^Number of delivery orders created: 1}, 'one delivery order created');
84
85 my $linked_dos = $sales_order->linked_records(to => 'DeliveryOrder');
86 is(scalar @$linked_dos, 1, 'one delivery order linked to order');
87 is($linked_dos->[0]->globalproject_id, $sales_order->globalproject_id, 'project ids match');
88
89 my $linked_items = $sales_order->items->[0]->linked_records(to => 'DeliveryOrderItem');
90 is(scalar @$linked_items, 1, 'one delivery order item linked to order item');
91 is($linked_items->[0]->qty*1, 3, 'qty in delivery order');
92 is($linked_items->[0]->base_qty*1, 3, 'base_qty in delivery order');
93
94 # reload order and orderitems to get changes to deliverd and ship
95 Rose::DB::Object::Helpers::forget_related($sales_order, 'orderitems');
96 $sales_order->load;
97
98 ok($sales_order->delivered, 'related order is delivered');
99 is($sales_order->items->[0]->ship*1, 3, 'ship in related order');
100
101 clear_up();
102
103
104 ########################################
105 # two time recordings, one order linked with project_id
106 # unit in order is 'min', but part is 'Std'
107 ########################################
108 $part     = new_service(partnumber => 'Serv1', unit => 'Std')->save;
109 $project  = create_project(projectnumber => 'p1', description => 'Project 1');
110 $customer = new_customer()->save;
111
112 $sales_order = create_sales_order(
113   save             => 1,
114   customer         => $customer,
115   globalproject    => $project,
116   taxincluded      => 0,
117   orderitems       => [ create_order_item(part => $part, qty => 180, unit => 'min', sellprice => 70), ]
118 );
119
120 @time_recordings = ();
121 push @time_recordings, new_time_recording(
122   start_time => DateTime->new(year => 2021, month =>  4, day => 19, hour => 10, minute => 10),
123   end_time   => DateTime->new(year => 2021, month =>  4, day => 19, hour => 11, minute => 10),
124   customer   => $customer,
125   project    => $project,
126   part       => $part,
127 )->save;
128 push @time_recordings, new_time_recording(
129   start_time => DateTime->new(year => 2021, month =>  4, day => 19, hour => 12, minute => 10),
130   end_time   => DateTime->new(year => 2021, month =>  4, day => 19, hour => 14, minute => 10),
131   customer   => $customer,
132   project    => $project,
133   part       => $part,
134 )->save;
135
136 # two time recordings, one order linked with project_id
137 %data = (
138   link_order => 1,
139   project_id => $project->id,
140   from_date  => '01.04.2021',
141   to_date    => '30.04.2021',
142 );
143 $db_obj = SL::DB::BackgroundJob->new();
144 $db_obj->set_data(%data);
145 $job    = SL::BackgroundJob::ConvertTimeRecordings->new;
146 $ret    = $job->run($db_obj);
147
148 $linked_dos = $sales_order->linked_records(to => 'DeliveryOrder');
149 $linked_items = $sales_order->items->[0]->linked_records(to => 'DeliveryOrderItem');
150 is($linked_items->[0]->qty*1, 3, 'different units: qty in delivery order');
151 is($linked_items->[0]->base_qty*1, 3, 'different units: base_qty in delivery order');
152
153 # reload order and orderitems to get changes to deliverd and ship
154 Rose::DB::Object::Helpers::forget_related($sales_order, 'orderitems');
155 $sales_order->load;
156
157 ok($sales_order->delivered, 'different units: related order is delivered');
158 is($sales_order->items->[0]->ship*1, 180, 'different units: ship in related order');
159
160 clear_up();
161
162
163 ########################################
164 # two time recordings, one order linked with project_id
165 # unit in order is 'Std', but part is 'min'
166 ########################################
167 $part     = new_service(partnumber => 'Serv1', unit => 'min')->save;
168 $project  = create_project(projectnumber => 'p1', description => 'Project 1');
169 $customer = new_customer()->save;
170
171 $sales_order = create_sales_order(
172   save             => 1,
173   customer         => $customer,
174   globalproject    => $project,
175   taxincluded      => 0,
176   orderitems       => [ create_order_item(part => $part, qty => 2, unit => 'Std', sellprice => 70), ]
177 );
178
179 @time_recordings = ();
180 push @time_recordings, new_time_recording(
181   start_time => DateTime->new(year => 2021, month =>  4, day => 19, hour => 10, minute => 10),
182   end_time   => DateTime->new(year => 2021, month =>  4, day => 19, hour => 11, minute => 10),
183   customer   => $customer,
184   project    => $project,
185   part       => $part,
186 )->save;
187 push @time_recordings, new_time_recording(
188   start_time => DateTime->new(year => 2021, month =>  4, day => 19, hour => 12, minute => 10),
189   end_time   => DateTime->new(year => 2021, month =>  4, day => 19, hour => 13, minute => 10),
190   customer   => $customer,
191   project    => $project,
192   part       => $part,
193 )->save;
194
195 # two time recordings, one order linked with project_id
196 %data = (
197   link_order => 1,
198   project_id => $project->id,
199   from_date  => '01.04.2021',
200   to_date    => '30.04.2021',
201 );
202 $db_obj = SL::DB::BackgroundJob->new();
203 $db_obj->set_data(%data);
204 $job    = SL::BackgroundJob::ConvertTimeRecordings->new;
205 $ret    = $job->run($db_obj);
206
207 $linked_dos = $sales_order->linked_records(to => 'DeliveryOrder');
208 $linked_items = $sales_order->items->[0]->linked_records(to => 'DeliveryOrderItem');
209 is($linked_items->[0]->qty*1, 2, 'different units 2: qty in delivery order');
210 is($linked_items->[0]->base_qty*1, 120, 'different units 2: base_qty in delivery order');
211
212 # reload order and orderitems to get changes to deliverd and ship
213 Rose::DB::Object::Helpers::forget_related($sales_order, 'orderitems');
214 $sales_order->load;
215
216 ok($sales_order->delivered, 'different units 2: related order is delivered');
217 is($sales_order->items->[0]->ship*1, 2, 'different units 2: ship in related order');
218
219 clear_up();
220
221
222 ########################################
223 # two time recordings, one with start/end one with date/duration
224 ########################################
225 $part     = new_service(partnumber => 'Serv1', unit => 'min')->save;
226 $customer = new_customer()->save;
227
228 @time_recordings = ();
229 push @time_recordings, new_time_recording(
230   start_time => DateTime->new(year => 2021, month =>  4, day => 19, hour => 10, minute => 10),
231   end_time   => DateTime->new(year => 2021, month =>  4, day => 19, hour => 11, minute => 10),
232   customer   => $customer,
233   part       => $part,
234 )->save;
235
236 push @time_recordings, new_time_recording(
237   date       => DateTime->new(year => 2021, month =>  4, day => 19),
238   duration   => 120,
239   start_time => undef,
240   end_time   => undef,
241   customer   => $customer,
242   part       => $part,
243 )->save;
244
245 %data = (
246   link_order => 0,
247   from_date  => '01.04.2021',
248   to_date    => '30.04.2021',
249 );
250 $db_obj = SL::DB::BackgroundJob->new();
251 $db_obj->set_data(%data);
252 $job    = SL::BackgroundJob::ConvertTimeRecordings->new;
253 $ret    = $job->run($db_obj);
254
255 my $dos = SL::DB::Manager::DeliveryOrder->get_all(where => [customer_id => $customer->id]);
256 is($dos->[0]->items->[0]->qty*1, 180/60, 'date/duration and start/end: qty in delivery order');
257 is($dos->[0]->items->[0]->base_qty*1, 180, 'date/duration and start/end2: base_qty in delivery order');
258
259 clear_up();
260
261
262 ########################################
263 # time recording, linked with order_id
264 ########################################
265 $part     = new_service(partnumber => 'Serv1', unit => 'Std')->save;
266 $customer = new_customer()->save;
267
268 # sales order with globalproject_id
269 $sales_order = create_sales_order(
270   save             => 1,
271   customer         => $customer,
272   taxincluded      => 0,
273   orderitems       => [ create_order_item(part => $part, qty => 3, sellprice => 70), ]
274 );
275
276 @time_recordings = ();
277 push @time_recordings, new_time_recording(
278   start_time => DateTime->new(year => 2021, month =>  4, day => 19, hour => 10, minute =>  5),
279   end_time   => DateTime->new(year => 2021, month =>  4, day => 19, hour => 11, minute =>  5),
280   customer   => $customer,
281   order      => $sales_order,
282   part       => $part,
283 )->save;
284
285 %data = (
286   link_order      => 1,
287   from_date       => '01.04.2021',
288   to_date         => '30.04.2021',
289   customernumbers => [$customer->number],
290 );
291 $db_obj = SL::DB::BackgroundJob->new();
292 $db_obj->set_data(%data);
293 $job    = SL::BackgroundJob::ConvertTimeRecordings->new;
294 $ret    = $job->run($db_obj);
295
296 is_deeply($job->{job_errors}, [], 'no errros');
297 like($ret, qr{^Number of delivery orders created: 1}, 'linked by order_id: one delivery order created');
298
299 $linked_dos = $sales_order->linked_records(to => 'DeliveryOrder');
300 is(scalar @$linked_dos, 1, 'linked by order_id: one delivery order linked to order');
301
302 $linked_items = $sales_order->items->[0]->linked_records(to => 'DeliveryOrderItem');
303 is(scalar @$linked_items, 1, 'linked by order_id: one delivery order item linked to order item');
304 is($linked_items->[0]->qty*1, 1, 'linked by order_id: qty in delivery order');
305 is($linked_items->[0]->base_qty*1, 1, 'linked by order_id: base_qty in delivery order');
306
307 # reload order and orderitems to get changes to deliverd and ship
308 Rose::DB::Object::Helpers::forget_related($sales_order, 'orderitems');
309 $sales_order->load;
310
311 is($sales_order->items->[0]->ship*1, 1, 'linked by order_id: ship in related order');
312
313 clear_up();
314
315
316 ########################################
317 # check rounding
318 ########################################
319 $part     = new_service(partnumber => 'Serv1', unit => 'Std')->save;
320 $customer = new_customer()->save;
321
322 $sales_order = create_sales_order(
323   save             => 1,
324   customer         => $customer,
325   taxincluded      => 0,
326   orderitems       => [ create_order_item(part => $part, qty => 3, sellprice => 70), ]
327 );
328
329 @time_recordings = ();
330 push @time_recordings, new_time_recording(
331   start_time => DateTime->new(year => 2021, month =>  4, day => 19, hour => 10, minute =>  0),
332   end_time   => DateTime->new(year => 2021, month =>  4, day => 19, hour => 10, minute =>  6),
333   customer   => $customer,
334   order      => $sales_order,
335   part       => $part,
336 )->save;
337
338 %data   = (
339   from_date  => '01.01.2021',
340   to_date    => '30.04.2021',
341   link_order => 1,
342   rounding   => 1,
343 );
344 $db_obj = SL::DB::BackgroundJob->new();
345 $db_obj->set_data(%data);
346 $job    = SL::BackgroundJob::ConvertTimeRecordings->new;
347 $ret    = $job->run($db_obj);
348
349 $linked_dos   = $sales_order->linked_records(to => 'DeliveryOrder');
350 $linked_items = $sales_order->items->[0]->linked_records(to => 'DeliveryOrderItem');
351 is($linked_items->[0]->qty*1, 0.25, 'rounding to quarter hour: qty in delivery order');
352 is($linked_items->[0]->base_qty*1, 0.25, 'rounding to quarter hour: base_qty in delivery order');
353
354 # reload order and orderitems to get changes to deliverd and ship
355 Rose::DB::Object::Helpers::forget_related($sales_order, 'orderitems');
356 $sales_order->load;
357
358 is($sales_order->items->[0]->ship*1, 0.25, 'rounding to quarter hour: ship in related order');
359
360 clear_up();
361
362
363 ########################################
364 # check rounding
365 ########################################
366 $part     = new_service(partnumber => 'Serv1', unit => 'Std')->save;
367 $customer = new_customer()->save;
368
369 $sales_order = create_sales_order(
370   save             => 1,
371   customer         => $customer,
372   taxincluded      => 0,
373   orderitems       => [ create_order_item(part => $part, qty => 3, sellprice => 70), ]
374 );
375
376 @time_recordings = ();
377 push @time_recordings, new_time_recording(
378   start_time => DateTime->new(year => 2021, month =>  4, day => 19, hour => 10, minute =>  0),
379   end_time   => DateTime->new(year => 2021, month =>  4, day => 19, hour => 10, minute =>  6),
380   customer   => $customer,
381   order      => $sales_order,
382   part       => $part,
383 )->save;
384
385 %data   = (
386   from_date  => '01.01.2021',
387   to_date    => '30.04.2021',
388   link_order => 1,
389   rounding   => 0,
390 );
391 $db_obj = SL::DB::BackgroundJob->new();
392 $db_obj->set_data(%data);
393 $job    = SL::BackgroundJob::ConvertTimeRecordings->new;
394 $ret    = $job->run($db_obj);
395
396 $linked_dos   = $sales_order->linked_records(to => 'DeliveryOrder');
397 $linked_items = $sales_order->items->[0]->linked_records(to => 'DeliveryOrderItem');
398 is($linked_items->[0]->qty*1, 0.1, 'no rounding: qty in delivery order');
399 is($linked_items->[0]->base_qty*1, 0.1, 'no rounding: base_qty in delivery order');
400
401 # reload order and orderitems to get changes to deliverd and ship
402 Rose::DB::Object::Helpers::forget_related($sales_order, 'orderitems');
403 $sales_order->load;
404
405 is($sales_order->items->[0]->ship*1, 0.1, 'no rounding: ship in related order');
406
407 clear_up();
408
409
410 ########################################
411 # are wrong params detected?
412 ########################################
413 %data = (
414   from_date       => 'x01.04.2021',
415 );
416 $db_obj = SL::DB::BackgroundJob->new();
417 $db_obj->set_data(%data);
418 $job    = SL::BackgroundJob::ConvertTimeRecordings->new;
419
420 my $err_msg = '';
421 eval { $ret = $job->run($db_obj);  1; } or do {$err_msg = $@};
422 ok($err_msg =~ '^Cannot convert date from string', 'wrong date string detected');
423
424 #####
425
426 $customer = new_customer()->save;
427 %data = (
428   customernumbers => ['a fantasy', $customer->number],
429 );
430
431 $db_obj = SL::DB::BackgroundJob->new();
432 $db_obj->set_data(%data);
433 $job    = SL::BackgroundJob::ConvertTimeRecordings->new;
434
435 $err_msg = '';
436 eval { $ret = $job->run($db_obj);  1; } or do {$err_msg = $@};
437 ok($err_msg =~ '^Not all customer numbers are valid', 'wrong customer number detected');
438
439 #####
440
441 %data = (
442   customernumbers => '123',
443 );
444
445 $db_obj = SL::DB::BackgroundJob->new();
446 $db_obj->set_data(%data);
447 $job    = SL::BackgroundJob::ConvertTimeRecordings->new;
448
449 $err_msg = '';
450 eval { $ret = $job->run($db_obj);  1; } or do {$err_msg = $@};
451 ok($err_msg =~ '^Customer numbers must be given in an array', 'wrong customer number data type detected');
452
453 #####
454
455 %data = (
456   part_id => '123',
457 );
458
459 $db_obj = SL::DB::BackgroundJob->new();
460 $db_obj->set_data(%data);
461 $job    = SL::BackgroundJob::ConvertTimeRecordings->new;
462
463 $err_msg = '';
464 eval { $ret = $job->run($db_obj);  1; } or do {$err_msg = $@};
465 ok($err_msg =~ '^No valid part found by given part id', 'invalid part id detected');
466
467 #####
468
469 $part = new_service(partnumber => 'Serv1', unit => 'Std', obsolete => 1)->save;
470 %data = (
471   part_id => $part->id,
472 );
473
474 $db_obj = SL::DB::BackgroundJob->new();
475 $db_obj->set_data(%data);
476 $job    = SL::BackgroundJob::ConvertTimeRecordings->new;
477
478 $err_msg = '';
479 eval { $ret = $job->run($db_obj);  1; } or do {$err_msg = $@};
480 ok($err_msg =~ '^No valid part found by given part id', 'obsolete part detected');
481
482 #####
483
484 %data = (
485   project_id => 123,
486 );
487
488 $db_obj = SL::DB::BackgroundJob->new();
489 $db_obj->set_data(%data);
490 $job    = SL::BackgroundJob::ConvertTimeRecordings->new;
491
492 $err_msg = '';
493 eval { $ret = $job->run($db_obj);  1; } or do {$err_msg = $@};
494 ok($err_msg =~ '^No valid project found by given project id', 'invalid project id detected');
495
496 #####
497
498 $project = create_project(projectnumber => 'p1', description => 'Project 1', valid => 0)->save;
499 %data = (
500   project_id => $project->id,
501 );
502
503 $db_obj = SL::DB::BackgroundJob->new();
504 $db_obj->set_data(%data);
505 $job    = SL::BackgroundJob::ConvertTimeRecordings->new;
506
507 $err_msg = '';
508 eval { $ret = $job->run($db_obj);  1; } or do {$err_msg = $@};
509 ok($err_msg =~ '^No valid project found by given project id', 'invalid project detected');
510
511 #####
512
513 clear_up();
514
515
516 ########################################
517
518 $::locale = $old_locale;
519
520 1;
521
522 #####
523 # vim: ft=perl
524 # set emacs to perl mode
525 # Local Variables:
526 # mode: perl
527 # End: