Completed
Push — master ( 18e32d...008ba1 )
by Paul
07:53
created

absences_Entry::getNextValidDate()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 23
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 9
c 0
b 0
f 0
nc 4
nop 1
dl 0
loc 23
rs 8.5906
ccs 10
cts 10
cp 1
crap 5
1
<?php
2
/************************************************************************
3
 * OVIDENTIA http://www.ovidentia.org                                   *
4
 ************************************************************************
5
 * Copyright (c) 2003 by CANTICO ( http://www.cantico.fr )              *
6
 *                                                                      *
7
 * This file is part of Ovidentia.                                      *
8
 *                                                                      *
9
 * Ovidentia is free software; you can redistribute it and/or modify    *
10
 * it under the terms of the GNU General Public License as published by *
11
 * the Free Software Foundation; either version 2, or (at your option)  *
12
 * any later version.													*
13
 *																		*
14
 * This program is distributed in the hope that it will be useful, but  *
15
 * WITHOUT ANY WARRANTY; without even the implied warranty of			*
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.					*
17
 * See the  GNU General Public License for more details.				*
18
 *																		*
19
 * You should have received a copy of the GNU General Public License	*
20
 * along with this program; if not, write to the Free Software			*
21
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,*
22
 * USA.																	*
23
************************************************************************/
24
25
26
require_once dirname(__FILE__).'/vacincl.php';
27
require_once dirname(__FILE__).'/request.class.php';
28
require_once $GLOBALS['babInstallPath'].'utilit/urlincl.php';
29
30
31
/**
32
 * Vacation request
33
 *
34
 *
35
 * @property int	$id_user
36
 * @property string $createdOn
37
 * @property int	$createdBy
38
 * @property string	$date_begin
39
 * @property string	$date_end
40
 * @property int	$idfai
41
 * @property string	$comment
42
 * @property string	$date			Last modification date or approbation date (last action)
43
 * @property string	$status			'' : unconfirmed ; 'Y' : confirmed ; 'P' : previsional
44
 * @property string	$comment2		Commentaire approbateur
45
 * @property int	$id_approver
46
 * @property int	$folder
47
 * @property int	$creation_type
48
 * @property int	$archived
49
 * 
50
 *
51
 */
52
class absences_Entry extends absences_Request
53
{
54
55
	const CREATION_USER = 0;
56
	const CREATION_FIXED = 1;
57
58
59
60
61
	/**
62
	 * Memory for saved or unsaved elements of a vacation request
63
	 * @var array
64
	 */
65
	private $elements = array();
66
67
68
	/**
69
	 * Memory for working periods of a vacation request
70
	 * they are the working periods saved with the entry at the creation of the request
71
	 * the periods will remain event after a workschedule modification for the user
72
	 * @var array
73
	 */
74
	private $plannedPeriods = array();
75
76
	/**
77
	 * @var float
78
	 */
79
	private $duration_days = null;
80
81
	/**
82
	 * @var float
83
	 */
84
	private $duration_hours = null;
85
86
87
	/**
88
	 * Cache for actual working periods from calendar
89
	 * @var bab_CalendarPeriod[]
90
	 */
91
	private $working_periods;
92
93
94
	/**
95
	 * cache for total quantity
96
	 * @var float
97
	 */
98
	private $total_days = null;
99
100
	/**
101
	 * cache for total quantity
102
	 * @var float
103
	 */
104
	private $total_hours = null;
105
106
107
	/**
108
	 * cache for total quantity per type
109
	 * @var array
110
	 */
111
	private $total_type_days = null;
112
113
	/**
114
	 * cache for total quantity per type
115
	 * @var float
116
	 */
117
	private $total_type_hours = null;
118
119
120
121
	/**
122
	 * @var array
123
	 */
124
	private $workingPeriodIndex = null;
125
	
126
	
127
	/**
128
	 * @see absences_EntryPeriod::getDurationDays
129
	 * @var array
130
	 */
131
	public $_getDurationDays_halfDays = array();
132
133
134
135
	/**
136
	 * @return absences_Entry
137
	 */
138
	public static function getById($id)
139
	{
140
		$request = new absences_Entry();
141
		$request->id = $id;
142
143
		return $request;
144
	}
145
146
147
	/**
148
	 * (non-PHPdoc)
149
	 * @see absences_Record::getRow()
150
	 */
151 37
	public function getRow()
152
	{
153 37
		if (null === $this->row)
154 37
		{
155 11
			if (!isset($this->id))
156 11
			{
157
				throw new Exception('Failed to load entry, missing entry id');
158
			}
159
160 11
			global $babDB;
161 11
			$res = $babDB->db_query('SELECT * FROM absences_entries WHERE id='.$babDB->quote($this->id));
162 11
			$this->setRow($babDB->db_fetch_assoc($res));
163 11
		}
164
165 37
		return $this->row;
166
	}
167
168
169
	/**
170
	 * (non-PHPdoc)
171
	 * @see absences_Request::getRequestType()
172
	 *
173
	 * @return string
174
	 */
175
	public function getRequestType()
176
	{
177
		switch($this->creation_type)
178
		{
179
			case '1':
180
				return absences_translate('Fixed vacation');
181
182
			default:
183
			case '0':
0 ignored issues
show
Unused Code introduced by
case '0': return abs...('Requested vacation'); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
184
				return absences_translate('Requested vacation');
185
		}
186
187
188
189
	}
190
191
192
193
194
	public function getApprobationId()
195
	{
196
		if ($agent = $this->getAgent())
197
		{
198
			return $agent->getApprobationId();
199
		}
200
201
		return null;
202
	}
203
204
205
206
207
208
	/**
209
	 * Get elements stored in database
210
	 * @return absences_EntryElemIterator
211
	 */
212
	public function getElementsIterator()
213
	{
214
		$I = new absences_EntryElemIterator;
215
		$I->entry = $this;
216
217
		return $I;
218
	}
219
220
221 11
	public function getPlannedPeriodsIterator()
222
	{
223 11
	    $I = new absences_EntryPeriodIterator;
224 11
	    $I->entry = $this;
225
226 11
	    return $I;
227
	}
228
229
230
231
	/**
232
	 * Get entries within the same folder
233
	 * @return absences_EntryIterator
234
	 */
235 View Code Duplication
	public function getFolderEntriesIterator()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
236
	{
237
		if (!$this->folder)
238
		{
239
			return null;
240
		}
241
242
		$I = new absences_EntryIterator;
243
		$I->folder = $this->folder;
0 ignored issues
show
Documentation Bug introduced by
The property $folder was declared of type string, but $this->folder is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
244
245
		return $I;
246
	}
247
248
249
250
251
252
253
254
	/**
255
	 *
256
	 * @param string $message	Generated message
257
	 * @param string $comment	Author comment
258
	 */
259
	public function addElementsMovements($message, $comment = '')
260
	{
261
		foreach($this->elements as $elem)
262
		{
263
			/*@var $elem absences_EntryElem */
264
			$elem->addMovement($message, $comment);
265
		}
266
	}
267
268
269
270
271
	/**
272
	 * Load elements from database
273
	 * @throws Exception
274
	 */
275
	public function loadElements()
276
	{
277
		if (!isset($this->id))
278
		{
279
			throw new Exception('Failed to load entry elements, id missing');
280
		}
281
282
		$this->elements = array();
283
284
		foreach($this->getElementsIterator() as $entryElem)
285
		{
286
			/*@var $entryElem absences_EntryElem */
287
			$this->addElement($entryElem);
288
		}
289
	}
290
291
	/**
292
	 * Get the loaded elements
293
	 * @return array
294
	 */
295 14
	public function getElements()
296
	{
297 14
		return $this->elements;
298
	}
299
300
	/**
301
	 * Get element by right from the loaded elements
302
	 * @param int|absences_Right $right
303
	 * @return absences_EntryElem
304
	 */
305 19
	public function getElement($right)
306
	{
307 19
	    $id_right = $right;
308 19
	    if ($right instanceof absences_Right) {
309
	        $id_right = $right->id; 
310
	    }
311
	    
312
	    
313 19
	    if (empty($this->elements)) {
314
	        // not loaded
315
	        
316 5
	        require_once dirname(__FILE__).'/entry_elem.class.php';
317
	        
318 5
	        global $babDB;
319
	        
320 5
	        $res = $babDB->db_query('SELECT * 
321
	            FROM absences_entries_elem 
322
	            WHERE 
323 5
	               id_entry='.$babDB->quote($this->id).' 
324 5
	               AND id_right='.$babDB->quote($id_right));
325
	        
326 5
	        $row = $babDB->db_fetch_assoc($res);
327
	        
328 5
	        if (!$row) {
329
	            return null;
330
	        }
331
	        
332 5
	        $elem = new absences_EntryElem();
333 5
	        $elem->setRow($row);
334
	        
335 5
	        if ($right instanceof absences_Right) {
336
	           $elem->setRight($right);
337
	        }
338
	        
339 5
	        return $elem;
340
	    }
341
342
	    
343
	    // already loaded
344
	    
345 14
	    foreach($this->elements as $element) {
346 14
	        if ($element->id_right == $id_right) {
347 14
	            return $element;
348
	        }
349 11
	    }
350
351
	    return null;
352
	}
353
	
354
	
355
	
356
	/**
357
	 * Add a planned period
358
	 * @param absences_EntryPeriod $plannedPeriod
359
	 */
360
	public function addPeriod(absences_EntryPeriod $plannedPeriod)
361
	{
362
	    $this->plannedPeriods[] = $plannedPeriod;
363
	}
364
365
366
367
368
	/**
369
	 * Load the planned working periods from database
370
	 * @throws Exception
371
	 */
372 12
	public function loadPlannedPeriods()
373
	{
374 12
	    if (!isset($this->id))
375 12
	    {
376
	        throw new Exception('Failed to load entry periods, id missing');
377
	    }
378
379 11
	    $this->plannedPeriods = array();
380
381 11
	    $res = $this->getPlannedPeriodsIterator();
382
383 11
	    if (0 === $res->count()) {
384 11
	        return $this->createPlannedPeriods();
385
	    }
386
387
	    foreach($res as $plannedPeriod)
388
	    {
389
	        /*@var $plannedPeriod absences_EntryPeriod */
390
	        $this->addPeriod($plannedPeriod);
391
	    }
392
	}
393
394
	/**
395
	 * Get the loaded planned working periods
396
	 * @return absences_EntryPeriod[]
397
	 */
398 13
	public function getPlannedPeriods()
399
	{
400 13
	    return $this->plannedPeriods;
401
	}
402
403
404
	/**
405
	 * Get planned duration
406
	 *
407
	 * @param string $begin    Optional limit to use for duration
408
	 * @param string $end      Optional limit to use for duration
409
	 *
410
	 * @return float
411
	 */
412 4 View Code Duplication
	public function getPlannedDurationDays($begin = null, $end = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
413
	{
414 4
	    if (empty($this->plannedPeriods)) {
415
	        $this->loadPlannedPeriods();
416
	    }
417
418 4
	    $total = 0.0;
419
420 4
	    foreach ($this->getPlannedPeriods() as $entryPeriod) {
421 4
	        $total += $entryPeriod->getDurationDays($begin, $end);
422 4
	    }
423
	    
424
425
	    
426
	     
427
428 4
	    return $total;
429
	}
430
431
432
	/**
433
	 * Get planned duration
434
	 *
435
	 * @param string $begin    Optional limit to use for duration
436
	 * @param string $end      Optional limit to use for duration
437
	 *
438
	 * @return float
439
	 */
440 3 View Code Duplication
	public function getPlannedDurationHours($begin = null, $end = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
441
	{
442 3
	    if (empty($this->plannedPeriods)) {
443
	        $this->loadPlannedPeriods();
444
	    }
445
446 3
	    $total = 0.0;
447
448 3
	    foreach ($this->getPlannedPeriods() as $entryPeriod) {
449 3
	        $total += $entryPeriod->getDurationHours($begin, $end);
450 3
	    }
451
452 3
	    return $total;
453
	}
454
455
	
456 7
	public function loadDefaultValues()
457
	{
458
	    
459 7
	    if (!isset($this->createdOn)) {
460
	        $this->createdOn = date('Y-m-d H:i:s');
461
	    }
462
	    
463 7
	    if (!isset($this->date)) {
464
	        $this->date = date('Y-m-d H:i:s');
465
	    }
466
	    
467 7
	    if (!isset($this->comment)) {
468 7
	        $this->comment = '';
469 7
	    }
470
	    
471 7
	    if (!isset($this->comment2)) {
472 7
	        $this->comment2 = '';
473 7
	    }
474
	    
475 7
	    if (!isset($this->idfai)) {
476 7
	        $this->idfai = '0';
477 7
	    }
478
	    
479 7
	    if (!isset($this->status)) {
480
	        $this->status = 'N';
481
	    }
482
	    
483 7
	    if (!isset($this->id_approver)) {
484 7
	        $this->id_approver = '0';
485 7
	    }
486
	    
487 7
	    if (!isset($this->folder)) {
488 7
	        $this->folder = '0';
489 7
	    }
490
	    
491 7
	    if (!isset($this->creation_type)) {
492 7
	        $this->creation_type = '0';
493 7
	    }
494
	    
495 7
	}
496
497
498
	/**
499
	 * Save entry to database
500
	 * without validity checking
501
	 *
502
	 * @return bool
503
	 */
504 7
	public function save()
505
	{
506
		// save entry
507
508 7
		global $babDB;
509
510 7
        $this->loadDefaultValues();
511
512 7
		if (isset($this->id))
513 7
		{
514
			$req = "
515
				UPDATE absences_entries
516
				SET
517 2
					`date`			=".$babDB->quote($this->date).",
518 2
					date_begin		=".$babDB->quote($this->date_begin).",
519 2
					date_end		=".$babDB->quote($this->date_end).",
520 2
					comment			=".$babDB->quote($this->comment).",
521 2
					idfai			=".$babDB->quote($this->idfai).",
522 2
					status			=".$babDB->quote($this->status).",
523 2
					comment2		=".$babDB->quote($this->comment2).",
524 2
					id_approver		=".$babDB->quote($this->id_approver).",
525 2
					folder			=".$babDB->quote($this->folder).",
526 2
					creation_type	=".$babDB->quote($this->creation_type)."
527 2
			";
528
			
529 2
			if (isset($this->todelete))
530 2
			{
531
			    $req .= ", todelete=".$babDB->quote($this->todelete);
532
			}
533
534 2
			if (isset($this->appr_notified))
535 2
			{
536
				$req .= ", appr_notified=".$babDB->quote($this->appr_notified);
537
			}
538
539
540
			$req .= " WHERE
541 2
					id=".$babDB->quote($this->id)." ";
542
543 2
			$babDB->db_query($req);
544
545
546 2 View Code Duplication
		} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
547
548 7
			$babDB->db_query("
549
				INSERT INTO absences_entries
550
				(
551
					id_user,
552
					date_begin,
553
					date_end,
554
					comment,
555
					`createdOn`,
556
					`date`,
557
					idfai,
558
					status,
559
					comment2,
560
					id_approver,
561
					folder,
562
					creation_type
563
				)
564
				VALUES
565
				(
566 7
					".$babDB->quote($this->id_user).",
567 7
					".$babDB->quote($this->date_begin).",
568 7
					".$babDB->quote($this->date_end).",
569 7
					".$babDB->quote($this->comment).",
570 7
					".$babDB->quote($this->createdOn).",
571 7
					".$babDB->quote($this->date).",
572 7
					".$babDB->quote($this->idfai).",
573 7
					".$babDB->quote($this->status).",
574 7
					".$babDB->quote($this->comment2).",
575 7
					".$babDB->quote($this->id_approver).",
576 7
					".$babDB->quote($this->folder).",
577 7
					".$babDB->quote($this->creation_type)."
578
				)
579 7
			");
580
581 7
			$this->id = $babDB->db_insert_id();
582
		}
583
584
585 7
	}
586
587
588
	/**
589
	 * Sort the loaded element by right order, type name
590
	 */
591 23
	public function sortElements()
592
	{
593 23
	    usort($this->elements, array('absences_Entry', 'sortElem'));
594 23
	}
595
596 23
	private static function sortElem(absences_EntryElem $elem1, absences_EntryElem $elem2)
597
	{
598 23
	    $right1 = $elem1->getRight();
599 23
	    $right2 = $elem2->getRight();
600
601 23
	    if ($right1->sortkey > $right2->sortkey) {
602 22
	        return 1;
603
	    }
604
605 2
	    if ($right1->sortkey < $right2->sortkey) {
606 2
	        return -1;
607
	    }
608
609 1
	    $type1 = $right1->getType();
610 1
	    $type2 = $right2->getType();
611
612 1
	    return bab_compare(mb_strtolower($type1->name), mb_strtolower($type2->name));
613
	}
614
	
615
	
616
	/**
617
	 * Verifier si les dates des elements sont correctement parametres
618
	 * en cas de modificiation de la quantite, toujours mettre les dates a zero
619
	 * @return bool
620
	 */
621 29
	public function checkElementsDates()
622
	{
623 29
	    foreach($this->elements as $elem) {
624 29
	        if (!isset($elem->date_begin) || '0000-00-00 00:00:00' === $elem->date_begin) {
625 29
	            return false;
626
	        }
627
	        
628 10
	        if (!isset($elem->date_end) || '0000-00-00 00:00:00' === $elem->date_end) {
629
	            return false;
630
	        }
631 10
	    }
632
	    
633 10
	    return true;
634
	}
635
636
637
	/**
638
	 * Add dates to loaded elements using the user calendar
639
	 */
640 29
	public function setElementsDates()
641
	{
642 29
	    include_once $GLOBALS['babInstallPath']."utilit/dateTime.php";
643
644 29
	    if (0 === count($this->elements)) {
645
	        throw new absences_EntryException('No elements to set dates on, id_entry='.$this->id.', owner='.$this->getUserName());
646
	    }
647
	    
648 29
	    if ($this->checkElementsDates()) {
649
	        // dates allready set
650
	        // calling getFutureDate twice does not work
651 10
	        return;
652
	    }
653
654 29
	    if (1 === count($this->elements)) {
655 6
	        $element = reset($this->elements);
656
	        /*@var $element absences_EntryElem */
657 6
	        $element->date_begin = $this->date_begin;
658 6
	        $element->date_end = $this->date_end;
659 6
	        return;
660
	    }
661
	    
662 23
	    $this->sortElements();
663
        
664 23
	    $loop = BAB_DateTime::fromIsoDateTime($this->date_begin);
665
666 23
	    foreach($this->elements as $element) {
667
	        /*@var $element absences_EntryElem */
668 23
	        $element->date_begin = $loop->getIsoDateTime();
669
	        
670 23
	        $loop = $this->getFutureDate($loop, (float) $element->quantity, $element->getRight()->quantity_unit);
671 23
	        $element->date_end = $loop->getIsoDateTime();
672
	        
673 23
	        if ($element->date_end > $this->date_end) {
674 2
	            $element->date_end = $this->date_end;
675 2
	        }
676
	        
677 23
	        $loop = $this->getNextValidDate($loop);
678 23
	    }
679
	    
680
	    // round the last half day to the request period end
681 23
	    if ($this->getElementEndGap() <= 3600) {
682 21
	        $this->elements[count($this->elements)-1]->date_end = $this->date_end;
683 21
	    }
684 23
	}
685
	
686
	/**
687
	 * Get the gap beetween the last element end date and the period end date
688
	 * @return int
689
	 */
690 23
	protected function getElementEndGap()
691
	{
692 23
	    $computedLastDate = bab_mktime($this->elements[count($this->elements)-1]->date_end);
693 23
	    $periodEnd = bab_mktime($this->date_end);
694
	     
695 23
	    if ($periodEnd > $computedLastDate) {
696 3
	         return ($periodEnd - $computedLastDate);
697
	    }
698
	    
699 20
	    if ($computedLastDate > $periodEnd) {
700
	        return ($computedLastDate - $periodEnd);
701
	    }
702
	    
703 20
	    return 0;
704
	}
705
706
707
	/**
708
	 * set the date to the next valid date
709
	 * @param BAB_DateTime $date
710
	 * @return BAB_DateTime
711
	 */
712 23
	protected function getNextValidDate(BAB_DateTime $date)
713
	{
714 23
	    $moment = $date->getTimeStamp();
715
716 23
	    foreach ($this->getWorkingPeriods() as $period) {
0 ignored issues
show
Bug introduced by
The expression $this->getWorkingPeriods() of type null|array<integer,object<bab_CalendarPeriod>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
717
718 23
	        if ($moment >= $period->ts_end) {
719 23
	            continue;
720
	        }
721
722
	        // if the future date (end date of the element) is in a worked period
723
	        // the future date is valid as a next date
724 23
	        if ($moment < $period->ts_end && $moment > $period->ts_begin) {
725 10
	            return $date;
726
	        }
727
728
729 13
	        return BAB_DateTime::fromTimeStamp($period->ts_begin);
730 21
	    }
731
732
733 21
	    return $date;
734
	}
735
736
737
738
	/**
739
	 * Add quantity to startdate only on working periods
740
	 *
741
	 * @param BAB_DateTime $startdate
742
	 * @param float $quantity
743
	 * @param string $quantity_unit D|H
744
	 *
745
	 * @return BAB_DateTime
746
	 */
747 23
	protected function getFutureDate(BAB_DateTime $startdate, $quantity, $quantity_unit)
748
	{
749 23
	    if ('H' === $quantity_unit) {
750 4
	        return $this->getFutureDate_Hours($startdate, $quantity);
751
	    }
752
753 20
	    return $this->getFutureDate_Days($startdate, $quantity);
754
	}
755
756
757
	/**
758
	 * Split working periods
759
	 * @return array
760
	 */
761 18
	protected function getWorkingPeriodsFromDate(BAB_DateTime $startdate)
762
	{
763 18
	    $periods = array();
764 18
	    $start = $startdate->getTimeStamp();
765
766 18
	    foreach ($this->getWorkingPeriods() as $period) {
0 ignored issues
show
Bug introduced by
The expression $this->getWorkingPeriods() of type null|array<integer,object<bab_CalendarPeriod>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
767
768 18
	        if ($start >= $period->ts_end) {
769
	            // continue to the next worked period
770 15
	            continue;
771
	        }
772
773 18
	        if ($start > $period->ts_begin && $start < $period->ts_end) {
774 10
	            $period->setBeginDate($startdate);
775 10
	            $periods[] = $period;
776 10
	            continue;
777
	        }
778
779 18
	        $periods[] = $period;
780 18
	    }
781
782 18
	    return $periods;
783
	}
784
785
786
787
	/**
788
	 * Add quantity to startdate only on working periods
789
	 *
790
	 * @param BAB_DateTime $startdate
791
	 * @param float $seconds_to_add
792
	 *
793
	 * @return BAB_DateTime
794
	 */
795 18
	protected function getFutureDate_Seconds(BAB_DateTime $startdate, $seconds_to_add)
796
	{
797 18
	    $worked_total = 0; //seconds
798
799
800 18
	    foreach ($this->getWorkingPeriodsFromDate($startdate) as $period) {
801
802 18
	        $add_in_period = ($seconds_to_add - $worked_total);
803 18
	        $worked_total += $period->getDuration();
804
805 18
	        if ((int) $worked_total === (int) $seconds_to_add) {
806
	            // la duree de la periode de travail est egale a duree demandee
807
	            // en tenant compte des periodes de travail precedentes
808
809 17
	            return BAB_DateTime::fromTimeStamp($period->ts_end);
810
	        }
811
812
813 15
            if ($worked_total > $seconds_to_add) {
814
                // la date future se trouve a l'interieur d'une periode travaillee
815 10
                $futureDate = $period->ts_begin + $add_in_period;
816 10
                return BAB_DateTime::fromTimeStamp($futureDate);
817
            }
818
819
            // continue to the next worked period
820
821 15
	    }
822
823 2
	    return BAB_DateTime::fromIsoDateTime($this->date_end);
824
	}
825
826
827
828
829
830
831
	/**
832
	 * Add quantity to startdate only on working periods
833
	 *
834
	 * @param BAB_DateTime $startdate
835
	 * @param float $quantity  hours
836
	 *
837
	 * @return BAB_DateTime
838
	 */
839 4
	protected function getFutureDate_Hours(BAB_DateTime $startdate, $quantity)
840
	{
841 4
	    $seconds_to_add = $quantity * 3600;
842
843 4
	    return $this->getFutureDate_Seconds($startdate, $seconds_to_add);
844
	}
845
	
846
	
847
	
848
	
849
	/**
850
	 * Create half days from a date
851
	 * @param BAB_DateTime $startdate
852
	 * @return absences_HalfDay
853
	 */
854
	protected function getHalfDaysFrom(BAB_DateTime $startdate)
855
	{
856
	    require_once dirname(__FILE__).'/halfday.class.php';
857 20
	    
858
	    $start = $startdate->getTimeStamp();
859 20
	    $halfdays = array();
860 20
	    foreach ($this->getWorkingPeriods() as $period) {
0 ignored issues
show
Bug introduced by
The expression $this->getWorkingPeriods() of type null|array<integer,object<bab_CalendarPeriod>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
861 20
	         
862
	         
863
	        if ($start >= $period->ts_end) {
864 20
	            continue;
865 15
	        } 
866
	        
867
	        $type = $this->getPeriodType($period);
868
	        $d = date('Y-m-d', $period->ts_begin).' '.$type;
869
	        
870 20
	        
871
	         
872 20
	        if (!isset($halfdays[$d])) {
873
	             
874 20
	            $halfdays[$d] = new absences_HalfDay();
875 20
	            $halfdays[$d]->dtstart = BAB_DateTime::fromTimeStamp($period->ts_begin);
876 20
	            $halfdays[$d]->dtend = BAB_DateTime::fromTimeStamp($period->ts_end);
877
	            $halfdays[$d]->type = $type;
878 20
	        }
879 20
	         
880
	         
881
	        $halfdays[$d]->duration += $period->getDuration();
882 20
	         
883
	        if ($period->ts_end > $halfdays[$d]->dtend->getTimestamp()) {
884 20
	            $halfdays[$d]->dtend = BAB_DateTime::fromTimeStamp($period->ts_end);
885 20
	        }
886 20
	        
887
	        if ($period->ts_begin < $halfdays[$d]->dtstart->getTimestamp()) {
888
	            $halfdays[$d]->dtstart = BAB_DateTime::fromTimeStamp($period->ts_begin);
889 20
	        }
890
	        
891 20
	    }
892 19
	     
893 19
	    return $halfdays;
894
	}
895 20
	
896
	
897
	
898
	
899 20
	/**
900 20
	 * periodes travailles, durees et types a partir d'une date
901 20
	 * 
902 20
	 * @deprecated Use getHalfDaysFrom instead
903
	 * 
904
	 * duration: seconds
905
	 * type: AMPM | AM | PM
906 20
	 * dayend: timestamp
907
	 * 
908
	 * @param BAB_DateTime $startdate
909
	 * @return array
910
	 */
911
	protected function getWorkingTimeByDay(BAB_DateTime $startdate)
912
	{
913
	    $start = $startdate->getTimeStamp();
914
	    $days = array();
915
	    foreach ($this->getWorkingPeriods() as $period) {
0 ignored issues
show
Bug introduced by
The expression $this->getWorkingPeriods() of type null|array<integer,object<bab_CalendarPeriod>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
916
	        
917
	        
918
	        if ($start >= $period->ts_end) {
919
	            continue;
920
	        }
921
	        
922
	        
923
	        
924 20
	        $d = date('Y-m-d', $period->ts_begin);
925
	        
926
	        if (!isset($days[$d])) {
927 20
	            
928
	            $days[$d] = array(
929 20
	                'duration' => 0,
930 20
	                'type' => null,
931
	                'dayend' => null
932 20
	            );
933
	        }
934
	        
935 20
	            
936
	        $days[$d]['duration'] += $period->getDuration();
937
	        
938
	        if ($period->ts_end > $days[$d]['dayend']) {
939
	            $days[$d]['dayend'] = $period->ts_end;
940 20
	        }
941 20
	        
942 20
	        
943 20
	        $type = $this->getPeriodType($period);
944
	        
945 20 View Code Duplication
	        if ('AM' === $days[$d]['type'] && 'PM' === $type) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
946 20
	            $days[$d]['type'] = 'AMPM';
947
	        }
948 15
	        
949 View Code Duplication
	        if ('PM' === $days[$d]['type'] && 'AM' === $type) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
950 15
	            $days[$d]['type'] = 'AMPM';
951
	        }
952 15
	        
953
	        if (!isset($days[$d]['type'])) {
954 15
	            $days[$d]['type'] = $type;
955 15
	        }
956
	    }
957 15
	    
958 9
	    
959
	    
960 9
	    return $days;
961 12
	}
962
	
963
	
964 15
	
965
    
966 15
    
967
    
968
    private function debugDays(array $days)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
969 9
    {
970 15
        $line_d = array();
971
        $lineam = array();
972
        $linepm = array();
973
        $line_h = array();
974
        
975
        $pad = 4; 
976 15
        
977
        foreach ($days as $d => $arr) {
978
            $line_d[] = str_pad(substr($d, 8), $pad);
979
            $lineam[] = str_pad($arr['type'] !== 'PM' ? 'XX' : '', $pad);
980
            $linepm[] = str_pad($arr['type'] !== 'AM' ? 'XX' : '', $pad);
981
            $line_h[] = str_pad(round($arr['duration']/3600, 1).'H', $pad);
982
        }
983 7
        
984 7
        return "\n".implode('| ', $line_d)
985 6
            ."\n".implode('| ', $lineam)
986 6
            ."\n".implode('| ', $linepm)
987
            ."\n".implode('| ', $line_h)."\n";
988 7
    }
989 7
	
990
	
991
	
992
	
993 7
	/**
994
	 * Add quantity to startdate only on working periods
995
	 *
996
	 * @param BAB_DateTime $startdate
0 ignored issues
show
Documentation introduced by
There is no parameter named $startdate. Did you maybe mean $startDate?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
997
	 * @param float $quantity  days
998 8
	 *
999
	 * @return BAB_DateTime
1000
	 */
1001
	protected function getFutureDate_Days(BAB_DateTime $startDate, $quantity)
1002
	{
1003
	    var_dump($startDate->getIsoDateTime().' + '.$quantity);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($startDate->get...) . ' + ' . $quantity); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
1004
	    
1005 15
	    $endDate = $startDate;
1006 15
	    $halfdays = $this->getHalfDaysFrom($startDate);
1007 15
	    
1008 15
	    foreach ($halfdays as $halfDay) {
1009 15
	        if ($quantity < 0.5) {
1010
	            $endDate = $halfDay->getEndDateFromQuantity($quantity);
1011 3
	            break;
1012 3
	        }
1013 15
	        
1014
	        $quantity -= 0.5;
1015
	    }
1016
	    
1017 20
	    var_dump($endDate->getIsoDateTime().' + '.$quantity);
1018 6
	    
1019
	    // get quantity before startDate
1020
	    
1021 15
	    $first = reset($halfdays);
1022
1023
	    if ($first->dtstart->getTimestamp() < $startDate->getTimeStamp()) {
1024 15
	        $ignoredHalfDayStart = $startDate->getTimeStamp() - $first->dtstart->getTimestamp();
1025
	        
1026
	        $endDate->add($ignoredHalfDayStart, BAB_DATETIME_SECOND);
1027
	    }
1028
	    
1029 15
1030
	    return $endDate;
1031 15
	}
1032
	
1033
	
1034
	/**
1035
	 * Trouver le nombre de secondes a ajouter a une date pour une quantite 
1036
	 * @param array $lastdays      Jours restants sur la periode
1037
	 * @param float $lastquantity  Nombre de jours a ajouter, uniquement les jours incomplets de la fin de periode, les jours complets ont ete traites avant
1038
	 * 
1039
	 */
1040
	protected function getSecondsToAdd(Array $lastdays, $lastquantity)
1041 15
	{
1042
	    $seconds_to_add = 0;
1043 15
	    
1044
	    //var_dump('lastquantity: '.$lastquantity);
1045
	    
1046
	    // trouver les heures a partir de la quantite restante
1047
	    
1048
	    foreach($lastdays as $keyday => $arr) {
1049 15
	        
1050
	        $q = 'AMPM' === $arr['type'] ? 1.0 : 0.5;
1051 15
	    
1052
	        if ($lastquantity > $q) {
1053 15
	            $lastquantity -= $q;
1054
	            continue;
1055
	        }
1056
	    
1057
	        //var_dump(($arr['duration']/3600).' '.$lastquantity.' '.$arr['type'].' '.$q);
1058
	        
1059
	        if ($lastquantity == $q) {
1060 15
	            $seconds_to_add = $arr['duration'];
1061 1
	            break;
1062 1
	        }
1063
	    
1064
	        $seconds_to_add = $arr['duration'] * $lastquantity;
1065 14
	        break;
1066 14
	    }
1067 15
	    
1068
	    return $seconds_to_add;
1069 15
	}
1070
	
1071
	
1072
1073
	/**
1074
	 * Add quantity to startdate only on working periods
1075
	 * 
1076
	 * @deprecated probleme avec les periodes a cheval sur 12:00 conbinne aux quantites inferieurs a 1 jour
1077
	 *
1078
	 * @param BAB_DateTime $startdate
1079
	 * @param float $quantity  days
1080
	 *
1081
	 * @return BAB_DateTime
1082
	 */
1083
	protected function getFutureDate_Days_Old(BAB_DateTime $startdate, $quantity)
1084
	{
1085
	    
1086
	    $start = $startdate->getTimeStamp();
1087
1088
	    $periods_quantity = 0; // days
1089
1090
        // get the number of worked hours from startdate to the end of the day
1091
 
1092
	    foreach ($this->getWorkingPeriods() as $period) {
0 ignored issues
show
Bug introduced by
The expression $this->getWorkingPeriods() of type null|array<integer,object<bab_CalendarPeriod>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1093
1094
	        if ($start >= $period->ts_end) {
1095
	            // continue to the next worked period
1096
	            //var_dump('working period ignored, start='.bab_shortDate($start, true).' >= '.bab_shortDate($period->ts_end, true));
1097
	            continue;
1098
	        }
1099
1100
	        $periods_quantity += $this->getPeriodQuantity($startdate, $period);
1101
	        
1102
1103
	        if ($periods_quantity >= $quantity) {
1104
1105
	            $overhead = $periods_quantity - $quantity;
1106
1107
	            $futureDate = BAB_DateTime::fromTimeStamp($period->ts_end);
1108
1109
	            // var_dump($futureDate->getIsoDateTime().' overhead='.$overhead.' ('.$periods_quantity.' - '.$quantity.')');
1110
1111
	            if ($overhead) {
1112
	                $futureDate->less(
1113
	                    3600 * $this->getDayDurationInHours($futureDate->getIsoDate(), $overhead),
1114
	                    BAB_DATETIME_SECOND
1115
	                );
1116
	            }
1117
1118
	            if ($futureDate->getIsoDateTime() > $this->date_end) {
1119
	                $futureDate = BAB_DateTime::fromIsoDateTime($this->date_end);
1120
	            }
1121
1122
	            return $futureDate;
1123
	        }
1124
	    }
1125
1126
	    return BAB_DateTime::fromIsoDateTime($this->date_end);
1127
	}
1128
1129
1130
	/**
1131
	 * Create the planned periods in the entry boundaries
1132
	 * from the calendar working periods
1133
	 */
1134
	public function createPlannedPeriods()
1135 23
	{
1136
	    require_once dirname(__FILE__).'/entry_period.class.php';
1137 23
	    $this->plannedPeriods = array();
1138 23
1139
	    foreach ($this->getWorkingPeriods() as $workingPeriod) {
0 ignored issues
show
Bug introduced by
The expression $this->getWorkingPeriods() of type null|array<integer,object<bab_CalendarPeriod>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1140 23
1141
	        /*@var $workingPeriod bab_CalendarPeriod */
1142
1143
	        $plannedPeriod = new absences_EntryPeriod();
1144 23
	        $plannedPeriod->setEntry($this);
1145 23
	        $plannedPeriod->date_begin = date('Y-m-d H:i:s', $workingPeriod->ts_begin);
1146 23
	        $plannedPeriod->date_end = date('Y-m-d H:i:s', $workingPeriod->ts_end);
1147 23
1148
	        $this->plannedPeriods[] = $plannedPeriod;
1149 23
	    }
1150 23
	}
1151 23
1152
1153
	/**
1154
	 * Get period quantity in days from a datetime
1155
	 *
1156
	 * @param BAB_DateTime         $startdate
1157
	 * @param bab_CalendarPeriod   $p
1158
	 *
1159
	 * @return float
1160
	 */
1161
	protected function getPeriodQuantity(BAB_DateTime $startdate, bab_CalendarPeriod $p)
1162
	{
1163
	    $type = $this->getPeriodType($p);
1164
1165
	    if ('AMPM' === $type) {
1166
	        $period_days = 1.0;
1167
	    }
1168
1169
	    if ('AM' === $type || 'PM' === $type) {
1170
	        $period_days = 0.5;
1171
	    }
1172
1173
	    $moment = $startdate->getTimeStamp();
1174
	    
1175
	    
1176
	    if ($moment > $p->ts_begin && $moment < $p->ts_end) {
1177
	        $period_seconds = $p->getDuration();
1178
	        $new_period = clone $p;
1179
	        $new_period->setBeginDate($startdate);
1180
1181
	        $new_seconds = $new_period->getDuration();
1182
1183
	        $period_days = ($new_seconds * $period_days) / $period_seconds;
0 ignored issues
show
Bug introduced by
The variable $period_days does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1184
	    }
1185
1186
	    return $period_days;
1187
	}
1188
1189
1190
1191
1192
	/**
1193
	 * Convert days to hours
1194
	 * using the number of hours in a specified day
1195
	 * the number of days given as parameter should be less than 1
1196
	 *
1197
	 * @param string $date     Must be a date in the entry period
1198
	 * @param float $days
1199
	 *
1200
	 * @return float   Hours
1201
	 */
1202
	protected function getDayDurationInHours($date, $days)
1203
	{
1204
	    $hours_in_day = 0;
1205
1206
        foreach ($this->getWorkingPeriods() as $p) {
0 ignored issues
show
Bug introduced by
The expression $this->getWorkingPeriods() of type null|array<integer,object<bab_CalendarPeriod>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1207
            $pdate = date('Y-m-d', $p->ts_begin);
1208
1209
            if ($pdate !== $date) {
1210
                continue;
1211
            }
1212
1213
            $hours_in_day += ($p->getDuration()/3600);
1214
        }
1215
1216
        $hours = ($days * $hours_in_day);
1217
1218
        return $hours;
1219
	}
1220
1221
1222
1223
1224
	/**
1225
	 *
1226
	 *
1227
	 * @param bab_CalendarPeriod $period
1228
	 *
1229
	 * @return AM | PM | AMPM
1230
	 */
1231
	protected function getPeriodType(bab_CalendarPeriod $period)
1232 20
	{
1233
	    $endtype = date('A', $period->ts_end);
1234 20
	    $begintype = date('A', $period->ts_begin);
1235 20
1236
	    if ('AM' === $endtype || '12:00:00' === date('H:i:s', $period->ts_end)) { //  || 'AM' === $begintype  && '14:00:00' >= date('H:i:s', $period->ts_end)
1237 20
	        return 'AM';
1238 14
	    }
1239
	    
1240
	    if ('AM' === $begintype && '14:00:00' >= date('H:i:s', $period->ts_end)) {
1241 20
	        return 'AM';
1242 6
	    }
1243
1244
	    if ('PM' === $begintype || '12:00:00' === date('H:i:s', $period->ts_begin)) {
1245 20
	        return 'PM';
1246 20
	    }
1247
	    
1248
	    return 'AMPM';
1249
	}
1250
1251
1252
1253
1254
	/**
1255
	 * Save elements of entry to database
1256
	 *
1257
	 */
1258
	public function saveElements()
1259
	{
1260
		$processed_ids = array();
1261
1262
		foreach ($this->elements as $elem)
1263
		{
1264
			/*@var $elem absences_EntryElem */
1265
		    
1266
		    try {
1267
		    
1268
    			$elem->save();
1269
    			$processed_ids[] = $elem->id;
1270
    			
1271
		    } catch(Exception $e) {
1272
		        // fail to save one element, it will be deleted or not created
1273
		        bab_debug($e->getMessage());
1274
		    }
1275
		}
1276
1277
		// delete removed elements
1278
1279
		global $babDB;
1280
1281
		$babDB->db_query('DELETE FROM absences_entries_elem WHERE id_entry='.$babDB->quote($this->id).' AND id NOT IN('.$babDB->quote($processed_ids).')');
1282
1283
	}
1284
1285
1286
1287
1288
	public function savePlannedPeriods()
1289
	{
1290
	    if (empty($this->plannedPeriods)) {
1291
	        throw new Exception('Planned periods where not loaded or set');
1292
	    }
1293
1294
	    $processed_ids = array();
1295
1296
	    foreach ($this->plannedPeriods as $period)
1297
	    {
1298
	        /*@var $elem absences_EntryPeriod */
1299
	        $period->save();
1300
1301
	        $processed_ids[] = $period->id;
1302
	    }
1303
1304
	    global $babDB;
1305
1306
	    $babDB->db_query('DELETE FROM absences_entries_periods WHERE id_entry='.$babDB->quote($this->id).' AND id NOT IN('.$babDB->quote($processed_ids).')');
1307
1308
	}
1309
1310
1311
1312
	/**
1313
	 * Apply dynamic rights for all right involved in the entry
1314
	 */
1315
	public function applyDynamicRight()
1316
	{
1317
		require_once dirname(__FILE__).'/agent_right.class.php';
1318
1319
		$I = new absences_AgentRightManagerIterator();
1320
		$I->setAgent($this->getAgent());
1321
1322
		foreach ($I as $agentRight)
1323
		{
1324
			/*@var $agentRight absences_AgentRight */
1325
			$agentRight->applyDynamicRight();
1326
		}
1327
	}
1328
1329
1330
1331
1332
	/**
1333
	 * Add element to list
1334
	 * @param absences_EntryElem $elem
1335
	 */
1336
	public function addElement(absences_EntryElem $elem)
1337 39
	{
1338
		$this->elements[] = $elem;
1339 39
	}
1340 39
1341
	/**
1342
	 * Remove an element in the list
1343
	 * @param int $id_right
1344
	 *
1345
	 * @return bool
1346
	 */
1347
	public function removeElement($id_right)
1348
	{
1349
		foreach ($this->elements as $key => $elem)
1350
		{
1351
			if ($elem->id_right == $id_right)
1352
			{
1353
				unset($this->elements[$key]);
1354
				return true;
1355
			}
1356
		}
1357
1358
		return false;
1359
	}
1360
1361
1362
	/**
1363
	 * Set the working period index for the entry
1364
	 * Used in unit tests to set differents working periods
1365
	 * if not used, the workingPeriodIndex will be generated from the calendar API
1366
	 *
1367
	 */
1368
	public function setWorkingPeriodIndex(Array $index_working)
1369 38
	{
1370
	    $this->workingPeriodIndex = $index_working;
1371 38
	    return $this;
1372 38
	}
1373
1374
	/**
1375
	 * Get working period index for the user and for the period
1376
	 * @return array
1377
	 */
1378
	private function getWorkingPeriodIndex()
1379 35
	{
1380
	    if (!isset($this->workingPeriodIndex)) {
1381 35
1382
	        include_once $GLOBALS['babInstallPath']."utilit/dateTime.php";
1383
1384
	        list($index_working,, $is_free) = absences_getHalfDaysIndex(
1385
	            $this->id_user,
1386
	            BAB_DateTime::fromIsoDateTime($this->date_begin),
1387
	            BAB_DateTime::fromIsoDateTime($this->date_end),
1388
	            true
1389
	        );
1390
1391
1392
	        foreach ($index_working as $key => $period_list) {
1393
	            if (!isset($is_free[$key])) {
1394
	                unset($index_working[$key]);
1395
	            }
1396
	        }
1397
1398
	        $this->workingPeriodIndex = $index_working;
1399
	    }
1400
1401
	    return $this->workingPeriodIndex;
1402 35
	}
1403
1404
1405
1406
	/**
1407
	 * Number of free days and hours between two dates
1408
	 *
1409
	 */
1410
	private function loadDurations() {
1411 35
1412
		$this->duration_days 	= 0.0;
1413 35
		$this->duration_hours 	= 0.0;
1414 35
		$this->working_periods = array();
1415 35
1416
1417
1418
		$index_working = $this->getWorkingPeriodIndex();
1419 35
1420
1421
		foreach ($index_working as $key => $period_list) {
1422 35
1423
			$this->duration_days += 0.5;
1424 35
1425
			foreach($period_list as $p)
1426 35
			{
1427
				/*@var $p bab_CalendarPeriod */
1428
1429
				if ($p->getCollection() instanceof bab_WorkingPeriodCollection)
1430 35
				{
1431 35
				    $this->working_periods[] = $p;
1432 35
					$this->duration_hours 	+= ($p->getDuration() / 3600);
1433 35
				}
1434 35
			}
1435 35
		}
1436 35
	}
1437 35
1438
1439
	/**
1440
	 * Get list of working periods of the entry
1441
	 * @return bab_CalendarPeriod[]
1442
	 */
1443
	protected function getWorkingPeriods()
1444 35
	{
1445
	    if (!isset($this->working_periods)) {
1446 35
	        $this->loadDurations();
1447 35
	    }
1448 35
1449
	    return $this->working_periods;
1450 35
	}
1451
1452
1453
	/**
1454
	 * List of working periods for one day
1455
	 * @param string $date 10 chars
1456
	 *
1457
	 * @return bab_CalendarPeriod[]
1458
	 */
1459
	public function getDayWorkingPeriods($date)
1460
	{
1461
	    $return = array();
1462
	    foreach ($this->getWorkingPeriods() as $period) {
0 ignored issues
show
Bug introduced by
The expression $this->getWorkingPeriods() of type null|array<integer,object<bab_CalendarPeriod>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1463
	        if ($date === date('Y-m-d', $period->ts_begin)) {
1464
	            $return[] = $period;
1465
	        }
1466
	    }
1467
1468
	    return $return;
1469
	}
1470
1471
1472
	/**
1473
	 * List of working periods for one day
1474
	 * @param string $date 10 chars
1475
	 *
1476
	 * @return absences_EntryPeriod[]
1477
	 */
1478
	public function getDayPlannedPeriods($date)
1479
	{
1480
	    $return = array();
1481
	    foreach ($this->getPlannedPeriodsIterator() as $period) {
1482
	        if ($date === substr($period->date_begin, 0, 10)) {
1483
	            $return[] = $period;
1484
	        }
1485
	    }
1486
1487
	    return $return;
1488
	}
1489
1490
1491
1492
1493
	/**
1494
	 * Get period duration in days
1495
	 * @return float
1496
	 */
1497
	public function getDurationDays()
1498
	{
1499
		if (!isset($this->duration_days))
1500
		{
1501
			$this->loadDurations();
1502
		}
1503
1504
		return $this->duration_days;
1505
	}
1506
1507
	/**
1508
	 * Get period duration in hours
1509
	 * @return float
1510
	 */
1511
	public function getDurationHours()
1512
	{
1513
		if (!isset($this->duration_hours))
1514
		{
1515
			$this->loadDurations();
1516
		}
1517
1518
		return $this->duration_hours;
1519
	}
1520
1521
1522
	/**
1523
	 * Convert a number of days to hours
1524
	 * @param float $days
1525
	 * @return float
1526
	 */
1527 View Code Duplication
	private function daysToHours($days)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1528
	{
1529
	    
1530
	    
1531
		if (0 === (int) round(100 * $this->getTotalDays()))
1532
		{
1533
			return 0;
1534
		}
1535
1536
		$ratio = $this->getPlannedDurationHours() / $this->getPlannedDurationDays();
1537
		return round(($ratio * $days), 2);
1538
	}
1539
1540
	/**
1541
	 * Convert a number of hours to days
1542
	 * @param float $hours
1543
	 * @return float
1544
	 */
1545 View Code Duplication
	private function hoursToDays($hours)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1546
	{
1547
		if (0 == $this->getTotalHours())
1548
		{
1549
			return 0;
1550
		}
1551
1552
		$ratio = $this->getPlannedDurationDays() / $this->getPlannedDurationHours();
1553
		return round(($ratio * $hours), 2);
1554
	}
1555
1556
1557
1558
	/**
1559
	 * Compute totals for loaded elements
1560
	 */
1561
	private function loadedElementsTotal()
1562
	{
1563
		if (empty($this->elements))
1564
		{
1565
			$this->loadElements();
1566
		}
1567
1568
		$this->total_days = 0.0;
1569
		$this->total_hours = 0.0;
1570
		$this->total_type_days = array();
1571
		$this->total_type_hours = array();
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type double of property $total_type_hours.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1572
1573
		foreach($this->elements as $elem)
1574
		{
1575
			/*@var $elem absences_EntryElem */
1576
			$right = $elem->getRight();
1577
1578
			$quantity = (float) $elem->quantity;
1579
1580
			switch($right->quantity_unit)
1581
			{
1582
				case 'D':
1583
					$hours = $this->daysToHours($quantity);
1584
					$this->addQUantityInCache($right, $quantity, $hours);
1585
					break;
1586
				case 'H':
1587
				    $days = $this->hoursToDays($quantity);
1588
				    $this->addQUantityInCache($right, $days, $quantity);
1589
					break;
1590
			}
1591
		}
1592
	}
1593
1594
1595
	/**
1596
	 * Populate the cache variables for one element
1597
	 *
1598
	 * @param absences_Right $right
1599
	 * @param float $days
1600
	 * @param float $hours
1601
	 */
1602
	private function addQUantityInCache(absences_Right $right, $days, $hours)
1603
	{
1604
	    if (!isset($this->total_type_days[$right->id_type])) {
1605
	        $this->total_type_days[$right->id_type] = 0.0;
1606
	    }
1607
1608
	    if (!isset($this->total_type_hours[$right->id_type])) {
1609
	        $this->total_type_hours[$right->id_type] = 0.0;
1610
	    }
1611
1612
	    $this->total_days += $days;
1613
	    $this->total_hours += $hours;
1614
	    $this->total_type_days[$right->id_type] += $days;
1615
	    $this->total_type_hours[$right->id_type] += $hours;
1616
	}
1617
1618
1619
1620
1621
1622
	/**
1623
	 * Get sum of elements quantity, converted in days
1624
	 *
1625
	 * @param int $id_type Optional filter by type
1626
	 *
1627
	 * @return float
1628
	 */
1629 View Code Duplication
	public function getTotalDays($id_type = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1630
	{
1631
		if (!isset($this->total_days)) {
1632
			$this->loadedElementsTotal();
1633
		}
1634
1635
		if (isset($id_type)) {
1636
1637
		    if (!isset($this->total_type_days[$id_type])) {
1638
		        return 0.0;
1639
		    }
1640
1641
		    return $this->total_type_days[$id_type];
1642
		}
1643
1644
		return $this->total_days;
1645
	}
1646
1647
	/**
1648
	 * Get sum of elements quantity, converted in hours
1649
	 *
1650
	 * @param int $id_type Optional filter by type
1651
	 *
1652
	 * @return float
1653
	 */
1654 View Code Duplication
	public function getTotalHours($id_type = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1655
	{
1656
		if (!isset($this->total_hours)) {
1657
			$this->loadedElementsTotal();
1658
		}
1659
1660
		if (isset($id_type)) {
1661
1662
		    if (!isset($this->total_type_hours[$id_type])) {
1663
		        return 0.0;
1664
		    }
1665
1666
		    return $this->total_type_hours[$id_type];
1667
		}
1668
1669
		return $this->total_hours;
1670
	}
1671
1672
1673
1674
1675
	/**
1676
	 * Number of days on entry between two dates
1677
	 * si les dates de l'element sont a cheval sur la periode demandee
1678
	 * on utlise les heures travailles qui etait en vigeur au moment de la creation
1679
	 * de la demande
1680
	 *
1681
	 * @param string   $begin	Datetime
1682
	 * @param string   $end		Datetime
1683
	 * @param int      $id_type Optional filter by type
1684
	 *
1685
	 * @return float (days)
1686
	 */
1687 View Code Duplication
	public function getPlannedDaysBetween($begin, $end, $id_type = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1688 1
	{
1689
	    if (empty($this->elements))
1690 1
	    {
1691 1
	        $this->loadElements();
1692
	    }
1693
1694
	    $total = 0.0;
1695 1
	    foreach($this->elements as $elem)
1696 1
	    {
1697
	        /*@var $elem absences_EntryElem */
1698
	        $right = $elem->getRight();
1699 1
1700
	        if (isset($id_type) && (int) $id_type !== (int) $right->id_type) {
1701 1
	            continue;
1702
	        }
1703
1704
	        $quantity = $elem->getPlannedQuantityBetween($begin, $end);
1705 1
1706
	        if ('H' === $right->quantity_unit) {
1707 1
	            $days = $this->hoursToDays($quantity);
1708
	        } else {
1709
	            $days = $quantity;
1710 1
	        }
1711
1712
	        $total += $days;
1713 1
	    }
1714 1
1715
	    return $total;
1716 1
	}
1717
1718
1719
1720
1721
	/**
1722
	 * Number of hours on entry between two dates
1723
	 * si les dates de l'element sont a cheval sur la periode demandee
1724
	 * on utlise les heures travailles qui etait en vigeur au moment de la creation
1725
	 * de la demande
1726
	 *
1727
	 * @param string   $begin	Datetime
1728
	 * @param string   $end		Datetime
1729
	 * @param int      $id_type Optional filter by type
1730
	 *
1731
	 * @return float (hours)
1732
	 */
1733 View Code Duplication
	public function getPlannedHoursBetween($begin, $end, $id_type = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1734
	{
1735
	    if (empty($this->elements))
1736
	    {
1737
	        $this->loadElements();
1738
	    }
1739
1740
	    $total = 0.0;
1741
	    foreach($this->elements as $elem)
1742
	    {
1743
	        /*@var $elem absences_EntryElem */
1744
	        $right = $elem->getRight();
1745
1746
	        if (isset($id_type) && (int) $id_type !== (int) $right->id_type) {
1747
	            continue;
1748
	        }
1749
1750
	        $quantity = $elem->getPlannedQuantityBetween($begin, $end);
1751
1752
	        if ('D' === $right->quantity_unit) {
1753
	            $hours = $this->daysToHours($quantity);
1754
	        } else {
1755
	            $hours = $quantity;
1756
	        }
1757
1758
	        $total += $hours;
1759
	    }
1760
1761
	    return $total;
1762
	}
1763
1764
1765
1766
1767
1768
1769
1770
1771
	/**
1772
	 * Test if the loaded elements in entry contains rights in hours
1773
	 * @return bool
1774
	 */
1775
	public function containsHours()
1776
	{
1777
		foreach($this->elements as $elem)
1778
		{
1779
			if ('H' === $elem->getRight()->quantity_unit)
1780
			{
1781
				return true;
1782
			}
1783
		}
1784
1785
		return false;
1786
	}
1787
1788
1789
	/**
1790
	 * Test if loaded elements in entry require approval
1791
	 *
1792
	 * @return bool
1793
	 */
1794
	public function requireApproval()
1795
	{
1796
		if (empty($this->elements))
1797
		{
1798
			$this->loadElements();
1799
		}
1800
1801
		foreach($this->elements as $elem)
1802
		{
1803
			if (1 === (int) $elem->getRight()->require_approval)
1804
			{
1805
				return true;
1806
			}
1807
		}
1808
1809
		return false;
1810
	}
1811
	
1812
	
1813
	public function checkAvailable()
1814
	{
1815
	    $res = $this->getElementsIterator();
1816
	    foreach ($res as $element) {
1817
	        /*@var $element absences_EntryElem */
1818
	        if (!$element->isQuantityAvailable()) {
1819
	            throw new Exception(sprintf(absences_translate('Failed to submit this request, the quantity for right %s s not available'), $element->getRight()->description));
1820
	        }
1821
	    }
1822
	}
1823
1824
	
1825
	/**
1826
	 * Check elements validity
1827
	 * @throws UnexpectedValueException
1828
	 * 
1829
	 * @return int number of elements with quantity > 0
1830
	 */
1831
	protected function checkElementsValidity()
1832
	{
1833
	    $count = 0;
1834
	    foreach($this->elements as $elem)
1835
	    {
1836
	        $quantity = (int) round(100 * $elem->quantity);
1837
	        if ($quantity > 0)
1838
	        {
1839
	            $count++;
1840
	        }
1841
	    
1842
	        $elem->checkValidity();
1843
	    }
1844
	    
1845
	    return $count;
1846
	}
1847
	
1848
	
1849
	/**
1850
	 * throw an exception if there is another entry in the same period
1851
	 * @throws absences_EntryException
1852
	 */
1853
	protected function checkOtherEntriesValidity()
1854
	{
1855
	    $otherEntries = new absences_EntryIterator();
1856
	    $otherEntries->from = $this->date_begin;
1857
	    $otherEntries->to = $this->date_end;
1858
	    $otherEntries->users = array($this->id_user);
1859
	    
1860
	    
1861
	    foreach ($otherEntries as $otherEntry) {
1862
	    
1863
	        /* @var $otherEntry absences_Entry */
1864
	    
1865 View Code Duplication
	        if ($this->id != $otherEntry->id) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1866
	            $e = new absences_EntryException(absences_translate('There is allready an absence request in the period'));
1867
	            $e->entry = $this;
1868
	            $e->blocking = true;
1869
	            throw $e;
1870
	        }
1871
	    }
1872
	    
1873
	}
1874
1875
1876
	/**
1877
	 * Check request validity
1878
	 *
1879
	 * @throws absences_EntryException
1880
	 * @throws UnexpectedValueException
1881
	 * 
1882
	 * @return bool
1883
	 */
1884
	public function checkValidity()
1885
	{
1886
		// verify mandatory data
1887
1888
		if (!isset($this->id_user) || $this->id_user <= 0)
1889
		{
1890
			throw new UnexpectedValueException('Unexpected id_user');
1891
		}
1892
1893
1894
		$count = $this->checkElementsValidity();
1895
1896
1897 View Code Duplication
		if (0 === $count)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1898
		{
1899
			$e = new absences_EntryException(absences_translate('At least one vacation right must be used'));
1900
			$e->entry = $this;
1901
			$e->blocking = true;
1902
			throw $e;
1903
		}
1904
1905
1906
1907
		// test user validity
1908
1909
		require_once $GLOBALS['babInstallPath'].'utilit/userinfosincl.php';
1910
1911
		$creationdate = bab_userInfos::getCreationDate($this->id_user);
1912
1913
		if ($creationdate >= $this->date_end) {
1914
			$e = new absences_EntryException(sprintf(absences_translate('The vacation request end before user creation date : %s'), bab_shortDate(bab_mktime($creationdate))));
1915
			$e->entry = $this;
1916
			$e->blocking = true;
1917
			throw $e;
1918
		}
1919
1920
		// T8359 : si l'utilisateur est reactive la demande ne sera pas presente, il faudra reenregistrer le droit
1921
		// avant la correction de ce ticket, on creeais les demandes pour eviter ce probleme
1922
1923
		if (!bab_userInfos::isValid($this->id_user)) {
1924
1925
		    /*
1926
		    $e = new absences_EntryException(absences_translate('This user account is disabled'));
1927
		    $e->entry = $this;
1928
		    $e->blocking = true;
1929
		    throw $e;
1930
		    */
1931
1932
		    return false;
1933
		}
1934
1935
1936
		// test quantity / period duration in days
1937
1938
		$total = (int) round(100 * $this->getTotalDays());
1939
		$duration = (int) round(100 * $this->getDurationDays());
1940
1941
1942 View Code Duplication
		if (0 === $duration)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1943
		{
1944
			$e = new absences_EntryException(absences_translate('The selected period is not available'));
1945
			$e->entry = $this;
1946
			$e->blocking = true;
1947
			throw $e;
1948
		}
1949
1950
1951
1952
		if ($total !== $duration)
1953
		{
1954
			$e = new absences_EntryException(sprintf(absences_translate('The total quantity (%s) does not match the period duration (%s)'), absences_quantity($this->getTotalDays(), 'D'), absences_quantity($this->getDurationDays(), 'D')));
1955
			$e->entry = $this;
1956
			$e->blocking = !((bool) absences_getVacationOption('allow_mismatch'));
1957
			throw $e;
1958
		}
1959
1960
		// test quantity / period duration in hours only if there is one right in hours
1961
		/*
1962
		 *
1963
		 * Pas de verification du nombre d'heure pour le moment, la precision de la verification du nombre de jours devrais suffire a verifier aussi les heures
1964
		 * c'est ce qui a ete fait sur la partie en HTML lorsque les les tests etait fait uniquement cote client (vacuser.html template newvacation)
1965
		 *
1966
		if ($this->containsHours())
1967
		{
1968
			$total = (int) round(100 * $this->getTotalHours());
1969
			$duration = (int) round(100 * $this->getDurationHours());
1970
1971
			if ($total !== $duration)
1972
			{
1973
				$e = new absences_EntryException(absences_translate('The total quantity does not match the period duration in hours'));
1974
				$e->blocking = !((bool) absences_getVacationOption('allow_mismatch'));
1975
				throw $e;
1976
			}
1977
		}
1978
1979
		*/
1980
1981
1982
1983
		// verifier si la periode demandee n'est pas deja occuppe par une autre demande
1984
        $this->checkOtherEntriesValidity();
1985
		
1986
1987
		return true;
1988
	}
1989
1990
1991
	/**
1992
	 * (non-PHPdoc)
1993
	 * @see absences_Request::modifiedOn()
1994
	 *
1995
	 * @return string
1996
	 */
1997
	public function modifiedOn()
1998 1
	{
1999
		return $this->date;
2000 1
	}
2001
2002
	/**
2003
	 * Test if the entry is a fixed vacation
2004
	 * @return bool
2005
	 */
2006
	public function isFixed()
2007
	{
2008
		return (1 === (int) $this->creation_type);
2009
2010
	}
2011
2012
2013
2014
2015
2016
	/**
2017
	 * Test if entry is previsonal
2018
	 * @return bool
2019
	 */
2020
	public function isPrevisonal()
2021
	{
2022
		return ($this->status === 'P');
2023
	}
2024
2025
	/**
2026
	 * Test if at least one entry in folder is previsonal
2027
	 * @return boolean
2028
	 */
2029
	public function isFolderPrevisonal()
2030
	{
2031
		if (!$this->folder)
2032
		{
2033
			return false;
2034
		}
2035
2036
		$I = new absences_EntryIterator;
2037
		$I->folder = $this->folder;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->folder can also be of type integer. However, the property $folder is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
2038
		$I->status = 'P';
2039
2040
		return ($I->count() > 0);
2041
	}
2042
2043
	/**
2044
	 * Get the first entry in folder
2045
	 * @return absences_Entry
2046
	 */
2047 View Code Duplication
	public function getFolderFirst()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2048
	{
2049
		if (!$this->folder)
2050
		{
2051
			return null;
2052
		}
2053
2054
		$I = new absences_EntryIterator;
2055
		$I->folder = $this->folder;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->folder can also be of type integer. However, the property $folder is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
2056
2057
		foreach($I as $first)
2058
		{
2059
			return $first;
2060
		}
2061
2062
		return null;
2063
	}
2064
2065
2066
	/**
2067
	 * Process specific code when the request is rejected
2068
	 *
2069
	 */
2070
	protected function onReject()
2071
	{
2072
		$this->updateCalendar();
2073
	}
2074
2075
2076
	/**
2077
	 * Process specific code when the request is confirmed
2078
	 *
2079
	 */
2080
	public function onConfirm()
2081
	{
2082
		$this->updateCalendar();
2083
	}
2084
2085
2086
2087
	/**
2088
	 * Call the user calendar backend to update the event
2089
	 * call the event to notify about calendar event modification
2090
	 */
2091
	public function updateCalendar()
2092
	{
2093
		require_once $GLOBALS['babInstallPath'].'utilit/dateTime.php';
2094
2095
		// try to update event copy in other backend (caldav)
2096
2097
		$begin = BAB_DateTime::fromIsoDateTime($this->date_begin);
2098
		$end = BAB_DateTime::fromIsoDateTime($this->date_end);
2099
		$period = absences_getPeriod($this->id, $this->id_user,  $begin, $end);
2100
		if ($period) {
2101
2102
			// probably set a new description if the event has been approved or rejected
2103
			absences_setPeriodProperties($period, $this);
2104
2105
			// save copy of event to calendar backend (if caldav)
2106
			$period->save();
2107
		}
2108
2109
2110
		// Update calendar data overlapped with event
2111
2112
		$date_begin = bab_mktime($this->date_begin);
2113
		$date_end	= bab_mktime($this->date_end);
2114
2115
		include_once $GLOBALS['babInstallPath']."utilit/eventperiod.php";
2116
		$event = new bab_eventPeriodModified($date_begin, $date_end, $this->id_user);
2117
		$event->types = BAB_PERIOD_VACATION;
2118
		bab_fireEvent($event);
2119
	}
2120
2121
2122
	public function getTitle()
2123
	{
2124
	    if (isset($this->todelete) && $this->todelete) {
2125
	        return absences_translate('vacation request to delete');
2126
	    }
2127
	    
2128
		return absences_translate('vacation request');
2129
	}
2130
2131
2132 View Code Duplication
	public function getNotifyFields()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2133
	{
2134
		return array(
2135
			absences_translate('From') 		=> bab_shortDate(bab_mktime($this->date_begin)),
2136
			absences_translate('Until')		=> bab_shortDate(bab_mktime($this->date_end)),
2137
			absences_translate('Quantity') 	=> absences_quantity($this->getTotalDays(), 'D')
2138
		);
2139
	}
2140
2141
2142 View Code Duplication
	public function getYear()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2143
	{
2144
		$year = (int) substr($this->date_begin, 0, 4);
2145
2146
		if (0 === $year)
2147
		{
2148
			return null;
2149
		}
2150
2151
		return $year;
2152
	}
2153
2154
2155 View Code Duplication
	public function getArchiveYear()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2156
	{
2157
		require_once $GLOBALS['babInstallPath'].'utilit/dateTime.php';
2158
		$year = (int) substr($this->date_begin, 0, 4);
2159
2160
		if (0 === $year)
2161
		{
2162
			return null;
2163
		}
2164
2165
		$day = absences_getVacationOption('archivage_day');
2166
		$month = absences_getVacationOption('archivage_month');
2167
2168
		$currentYear = new BAB_DateTime($year, $month, $day);
2169
		if($this->date_begin < $currentYear->getIsoDate()){
2170
			$year = $year-1;
2171
		}
2172
2173
		return $year;
2174
	}
2175
2176 View Code Duplication
	public function archive()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2177
	{
2178
		global $babDB;
2179
2180
		$babDB->db_query("
2181
			UPDATE absences_entries
2182
			SET archived=".$babDB->quote(1)."
2183
			WHERE
2184
				id=".$babDB->quote($this->id)."
2185
		");
2186
	}
2187
2188
2189 View Code Duplication
	public function setNotified()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2190
	{
2191
		global $babDB;
2192
2193
		if (!$this->id)
2194
		{
2195
			throw new Exception('Missing ID');
2196
		}
2197
2198
		$babDB->db_query("
2199
			UPDATE absences_entries
2200
				SET appr_notified=".$babDB->quote(1)."
2201
			WHERE
2202
				id=".$babDB->quote($this->id)."
2203
		");
2204
	}
2205
2206
2207
	public function getManagerEditUrl()
2208
	{
2209
		$addon = bab_getAddonInfosInstance('absences');
2210
		$ts = bab_mktime($this->date_begin);
2211
		return $addon->getUrl().'vacuser&idx=period&id='.$this->id.'&year='.date('Y', $ts).'&month='.date('n', $ts).'&rfrom=1';
2212
	}
2213
2214
2215
	public function getManagerDeleteUrl()
2216
	{
2217
		$addon = bab_getAddonInfosInstance('absences');
2218
		$url = new bab_url($addon->getUrl().'vacadmb');
2219
		$url->idx = 'delete';
2220
		$url->id_entry = $this->id;
2221
		$url->from = $_SERVER['REQUEST_URI'];
2222
		return $url->toString();
2223
	}
2224
2225
2226
	public function getManagerFrame()
2227
	{
2228
		$W = bab_Widgets();
2229
		$vbox = $W->VBoxLayout();
2230
2231
		if ($this->isFixed())
2232
		{
2233
			$vbox->addItem($W->Icon(absences_translate('Fixed vacation period'), Func_Icons::ACTIONS_VIEW_CALENDAR_DAY));
2234
		} else {
2235
			$vbox->addItem($W->Link($W->Icon(absences_translate('Vacation request'), Func_Icons::ACTIONS_VIEW_LIST_DETAILS), absences_addon()->getUrl()."vacadmb&idx=morvw&id=".$this->id));
2236
		}
2237
		$vbox->addItem($W->Label(absences_DateTimePeriod($this->date_begin, $this->date_end)));
2238
2239
		return $vbox;
2240
	}
2241
2242
2243
	/**
2244
	 *
2245
	 * @param bool 			$display_username	Affiche le nom du demandeur dans la card frame ou non, utile pour les listes contenant plusieurs demandeurs possibles
2246
	 * @param int			$rfrom				si les demandes de la liste sont modifiee par un gestionnaire ou gestionnaire delegue
2247
 	 * @param int			$ide				id entite de l'organigramme >0 si gestionnaire delegue seulement
2248
	 */
2249
	public function getCardFrame($display_username, $rfrom, $ide)
2250
	{
2251
		bab_functionality::includeOriginal('Icons');
2252
2253
		$W = bab_Widgets();
2254
		$layout = $W->HBoxLayout()->setHorizontalSpacing(2,'em')->addClass('widget-full-width');
2255
		$frame = $W->Frame(null, $layout);
2256
2257
		$frame->addClass(Func_Icons::ICON_LEFT_16);
2258
		$frame->addClass('absences-entry-cardframe');
2259
		$layout->addItem($col1 = $W->VBoxLayout()->setVerticalSpacing(.5,'em'));
2260
		$layout->addItem($col2 = $W->VBoxLayout()->setVerticalSpacing(.3,'em'));
2261
2262
		if ($this->isPrevisonal())
2263
		{
2264
			$btn = $W->Link($W->Button()->addItem($W->Label(absences_translate('Submit for approval'))), absences_addon()->getUrl()."vacuser&idx=subprev&id_entry=".$this->id);
2265
			$btn->setSizePolicy('display-opened');
2266
			$layout->addItem($btn);
2267
		}
2268
2269
		if ($this->isFolderPrevisonal())
2270
		{
2271
			$first = $this->getFolderFirst();
2272
			if ($first && $first->id === $this->id)
2273
			{
2274
				$btn = $W->Link($W->Button()->addItem($W->Label(absences_translate('Submit requests for approval'))), absences_addon()->getUrl()."vacuser&idx=subprev&id_entry=".$this->id);
2275
				$btn->setSizePolicy('display-closed');
2276
				$layout->addItem($btn);
2277
			}
2278
		}
2279
2280
2281
		$layout->addItem($col3 = $W->VBoxLayout()->setVerticalSpacing(.5,'em'));
2282
2283
		if ($this->isFixed())
2284
		{
2285
			$col1->addItem($W->Icon(absences_translate('Fixed vacation period'), Func_Icons::ACTIONS_VIEW_CALENDAR_DAY));
2286
			$col1->setCanvasOptions($col1->Options()->width(25,'em'));
2287
		} else {
2288
			$col1->addItem($W->Link($W->Icon(absences_translate('Vacation request'), Func_Icons::ACTIONS_VIEW_LIST_DETAILS), absences_addon()->getUrl()."vacuser&idx=morve&id=".$this->id));
2289
			$col1->setCanvasOptions($col1->Options()->width(25,'em'));
2290
2291
			$col1->addItem($this->getStatusIcon());
2292
		}
2293
2294
		$col2->setSizePolicy(Widget_SizePolicy::MAXIMUM);
2295
2296
		$col2->addItem($W->Title(absences_DateTimePeriod($this->date_begin, $this->date_end), 5));
2297
2298
		if ($display_username)
2299
		{
2300
			$col2->addItem($this->labelledValue(absences_translate('Owner'), $this->getUserName()));
2301
		}
2302
2303
		$col2->addItem($this->labelledValue(absences_translate('Quantity'), absences_vacEntryQuantity($this->id)));
2304
2305
2306
2307
2308 View Code Duplication
		if ($this->canModify())
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2309
		{
2310
			$col3->addItem($W->Link($W->Icon(absences_translate('Modify'), Func_Icons::ACTIONS_DOCUMENT_EDIT), $this->getEditUrl($rfrom, $ide)));
2311
		}
2312
2313
		if ($this->canDelete())
2314
		{
2315
			$urldelete = absences_addon()->getUrl()."vacuser&idx=delete&id_entry=".$this->id;
2316
			$col3->addItem($W->Link($W->Icon(absences_translate('Delete'), Func_Icons::ACTIONS_EDIT_DELETE), $urldelete));
2317
		}
2318
2319
		$frame->setTitle(sprintf(absences_translate('Created the %s'), bab_shortDate(bab_mktime($this->createdOn()))));
2320
2321
		return $frame;
2322
	}
2323
2324
2325
2326
2327
2328
	/**
2329
	 * @return string
2330
	 */
2331
	public function getEditUrl($rfrom, $ide = null)
2332
	{
2333
		$begin_ts = bab_mktime($this->date_begin);
2334
		$url = absences_addon()->getUrl()."vacuser&idx=period&id=".$this->id."&year=".date('Y',$begin_ts)."&month=".date('n',$begin_ts);
2335
2336
		if (isset($rfrom))
2337
		{
2338
			$url .= '&rfrom='.$rfrom;
2339
		}
2340
2341
		if (isset($ide))
2342
		{
2343
			$url .= '&ide='.$ide;
2344
		}
2345
2346
		return $url;
2347
	}
2348
2349
2350
2351
	/**
2352
	 * Delete the vacation request
2353
	 * @return bool
2354
	 */
2355
	public function delete()
2356
	{
2357
2358
2359
2360
		global $babDB;
2361
2362
		if (!$this->getRow())
2363
		{
2364
			return false;
2365
		}
2366
2367
		parent::delete();
2368
		
2369
		if ('0000-00-00 00:00:00' !== $this->date_begin && '0000-00-00 00:00:00' !== $this->date_end) {
2370
    		include_once $GLOBALS['babInstallPath']."utilit/dateTime.php";
2371
    		$date_begin = BAB_DateTime::fromIsoDateTime($this->date_begin);
2372
    		$date_end	= BAB_DateTime::fromIsoDateTime($this->date_end);
2373
    
2374
    		$period = absences_getPeriod($this->id, $this->id_user, $date_begin, $date_end);
2375
    		if (null !== $period)
2376
    		{
2377
    			$period->delete();
2378
    		}
2379
		}
2380
		$babDB->db_query("DELETE FROM absences_dynamic_rights WHERE id_entry=".$babDB->quote($this->id));
2381
		$babDB->db_query("DELETE FROM ".ABSENCES_ENTRIES_ELEM_TBL." WHERE id_entry=".$babDB->quote($this->id)."");
2382
		$babDB->db_query("DELETE FROM ".ABSENCES_ENTRIES_TBL." WHERE id=".$babDB->quote($this->id));
2383
2384
		$this->applyDynamicRight();
2385
		
2386
		if (isset($date_begin) && isset($date_end)) {
2387
    		$cal_begin = clone $date_begin;
2388
    		$cal_end = clone $date_end;
2389
    
2390
    		$cal_end->add(1, BAB_DATETIME_MONTH);
2391
    
2392
    		while ($cal_begin->getTimeStamp() <= $cal_end->getTimeStamp()) {
2393
    			$month	= $cal_begin->getMonth();
2394
    			$year	= $cal_begin->getYear();
2395
    			absences_updateCalendar($this->id_user, $year, $month);
2396
    			$cal_begin->add(1, BAB_DATETIME_MONTH);
2397
    		}
2398
    		
2399
    		$this->addMovement(
2400
    		        sprintf(
2401
    		                absences_translate('The vacation entry from %s to %s has been deleted'),
2402
    		                $date_begin->shortFormat(true),
2403
    		                $date_end->shortFormat(true)
2404
    		        )
2405
    		);
2406
		}
2407
2408
		
2409
2410
		return true;
2411
	}
2412
2413
2414
2415
2416
2417
2418
2419
	/**
2420
	 * (non-PHPdoc)
2421
	 * @see absences_Request::notifyOwner()
2422
	 */
2423
	public function notifyOwner()
2424
	{
2425
		parent::notifyOwner();
2426
2427
		if ('Y' != $this->status)
2428
		{
2429
			return;
2430
		}
2431
2432
		$agent = $this->getAgent();
2433
		$emails = $agent->getEmails();
2434
2435
		if (empty($emails))
2436
		{
2437
			return;
2438
		}
2439
2440
		require_once dirname(__FILE__).'/request.notify.php';
2441
		absences_notifyEntryOwnerEmails(array($this), $emails);
2442
	}
2443
2444
}
2445
2446
2447
2448
2449
class absences_EntryException extends Exception
2450
{
2451
	/**
2452
	 * Optional entry associated to error (unsaved entry object)
2453
	 * @var absences_Entry
2454
	 */
2455
	public $entry;
2456
2457
2458
	/**
2459
	 * Define if the exception if blocking a save
2460
	 * @var bool
2461
	 */
2462
	public $blocking = true;
2463
}
2464
2465
2466
2467
/**
2468
 * Vacation requests
2469
 * Sorted by creation date
2470
 */
2471
class absences_EntryIterator extends absences_Iterator
2472
{
2473
2474
	/**
2475
	 *
2476
	 * @var string
2477
	 */
2478
	public $orderby = 'createdOn ASC';
2479
2480
2481
	/**
2482
	 *
2483
	 * @var string
2484
	 */
2485
	public $folder;
2486
2487
	/**
2488
	 *
2489
	 * @var array
2490
	 */
2491
	public $users;
2492
2493
2494
	/**
2495
	 *
2496
	 * @var mixed (array|string)
2497
	 */
2498
	public $status;
2499
2500
2501
	/**
2502
	 * Entry to ignore in the folder
2503
	 * @var int
2504
	 */
2505
	public $id_entry_folder;
2506
2507
2508
	/**
2509
	 * Filtrer les demandes necessitant ou pas un email aux approbateurs
2510
	 * @var int
2511
	 */
2512
	public $appr_notified;
2513
2514
	/**
2515
	 * Filtrer les demandes avec unes instance d'approbation
2516
	 * @var bool
2517
	 */
2518
	public $idfai_set;
2519
2520
2521
	/**
2522
	 * Filtrer les demandes avec une liste d'instances d'approbation
2523
	 * @var array
2524
	 */
2525
	public $appr_idfai;
2526
2527
2528
	/**
2529
	 * Search all request created before this date time
2530
	 * @var string
2531
	 */
2532
	public $createdOn;
2533
2534
	/**
2535
	 * Search all request modified before this date
2536
	 * @var string
2537
	 */
2538
	public $modifiedOn;
2539
2540
2541
	/**
2542
	 * Datetime
2543
	 * @var string
2544
	 */
2545
	public $from;
2546
2547
2548
	/**
2549
	 * Datetime
2550
	 * @var string
2551
	 */
2552
	public $to;
2553
2554
2555
	/**
2556
	 * Filtrer les demandes par annee
2557
	 * @var int
2558
	 */
2559
	public $year;
2560
2561
2562
	/**
2563
	 * Filtrer les demandes par organisation
2564
	 * @var int
2565
	 */
2566
	public $organization;
2567
2568
2569
	/**
2570
	 * Filtrer les demandes par statut d'archivage
2571
	 * @var int
2572
	 */
2573
	public $archived = 0;
2574
	
2575
	/**
2576
	 * with at least one element with this right
2577
	 */
2578
	public $id_right;
2579
2580
2581
2582
	public function getObject($data)
2583 6
	{
2584
2585
		$entry = new absences_Entry;
2586 6
		$entry->setRow($data);
2587 6
		return $entry;
2588 6
2589
	}
2590
2591
2592
2593
	public function executeQuery()
2594 21
	{
2595
		if(is_null($this->_oResult))
2596 21
		{
2597 21
			global $babDB;
2598 21
			$req = 'SELECT e.* 
2599
				FROM
2600
					absences_entries e 
2601
				';
2602 21
2603
2604
			$where = array();
2605 21
			if (isset($this->folder))
2606 21
			{
2607 21
				$where[] = 'e.folder='.$babDB->quote($this->folder);
2608
2609
				if (isset($this->id_entry_folder))
2610
				{
2611
					$where[] = 'e.id<>'.$babDB->quote($this->id_entry_folder);
2612
				}
2613
			}
2614
2615
			if (!empty($this->users))
2616 21
			{
2617 21
				$where[] = 'e.id_user IN('.$babDB->quote($this->users).')';
2618 21
			}
2619 21
2620
			if (isset($this->status))
2621 21
			{
2622 21
				$where[] = 'e.status IN('.$babDB->quote($this->status).')';
2623 7
			}
2624 7
2625
			if (isset($this->createdOn))
2626 21
			{
2627 21
				$where[] = 'e.createdOn<='.$babDB->quote($this->createdOn);
2628 15
			}
2629 15
2630
			if (isset($this->modifiedOn))
2631 21
			{
2632 21
				$where[] = 'e.`date`<='.$babDB->quote($this->modifiedOn);
2633
			}
2634
2635
			if (isset($this->appr_notified))
2636 21
			{
2637 21
				$where[] = 'e.appr_notified='.$babDB->quote($this->appr_notified);
2638
			}
2639
2640
			if (isset($this->idfai_set) && $this->idfai_set)
2641 21
			{
2642 21
				$where[] = 'e.idfai>'.$babDB->quote(0);
2643
			}
2644
2645
			if (isset($this->appr_idfai))
2646 21
			{
2647 21
				$where[] = 'e.idfai IN('.$babDB->quote($this->appr_idfai).')';
2648
			}
2649
2650
			if (isset($this->from))
2651 21
			{
2652 21
				$where[] = 'e.date_end >'.$babDB->quote($this->from);
2653
			}
2654
2655
			if (isset($this->to))
2656 21
			{
2657 21
				$where[] = 'e.date_begin <'.$babDB->quote($this->to);
2658
			}
2659
2660
			if (isset($this->startTo))
2661 21
			{
2662 21
				$where[] = 'e.date_begin <='.$babDB->quote($this->startTo);
0 ignored issues
show
Bug introduced by
The property startTo does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
2663
			}
2664
2665
			if (isset($this->startFrom))
2666 21
			{
2667 21
				$where[] = 'e.date_begin >='.$babDB->quote($this->startFrom);
0 ignored issues
show
Bug introduced by
The property startFrom does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
2668
			}
2669
2670
			if (isset($this->year))
2671 21
			{
2672 21
				$where[] = 'YEAR(e.date_begin)='.$babDB->quote($this->year);
2673
			}
2674
2675 View Code Duplication
			if (isset($this->organization) && $this->organization)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2676 21
			{
2677 21
				$req .= ', absences_personnel p';
2678
				$where[] = 'e.id_user=p.id_user';
2679
				$where[] = 'p.id_organization='.$babDB->quote($this->organization);
2680
			}
2681
2682
			if (isset($this->archived))
2683 21
			{
2684 21
				$where[] = 'e.archived='.$babDB->quote($this->archived);
2685 21
			}
2686 21
			
2687
			if (isset($this->id_right))
2688 21
			{
2689 21
			    $where[] = 'e.id IN(SELECT id_entry FROM absences_entries_elem WHERE id_right='.$babDB->quote($this->id_right).')';
2690 21
			}
2691 21
2692
			if ($where)
0 ignored issues
show
Bug Best Practice introduced by
The expression $where of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2693
			{
2694 21
				$req .= ' WHERE '.implode(' AND ', $where);
2695 21
			}
2696 21
2697
			$req .= ' ORDER BY '.$this->orderby;
2698 21
			
2699
			$this->setMySqlResult($this->getDataBaseAdapter()->db_query($req));
2700 21
		}
2701 21
	}
2702 21
}
2703
2704
2705
/**
2706
 * Entry elements iterator
2707
 */
2708
class absences_EntryElemIterator extends absences_Iterator
2709
{
2710
	/**
2711
	 *
2712
	 * @var absences_Entry
2713
	 */
2714
	public $entry;
2715
2716
	/**
2717
	 * Filter by a list of id vaction types
2718
	 * @var int[]
2719
	 */
2720
	public $types;
2721
2722
2723
	public function getObject($data)
2724
	{
2725
		require_once dirname(__FILE__).'/type.class.php';
2726
		require_once dirname(__FILE__).'/right.class.php';
2727
		require_once dirname(__FILE__).'/entry_elem.class.php';
2728
2729
		$type_row = $this->getRowByPrefix($data, 'type');
2730
		$type = new absences_Type($type_row['id']);
2731
		$type->setRow($type_row);
2732
2733
		$right_row = $this->getRowByPrefix($data, 'right');
2734
		$right = new absences_Right($right_row['id']);
2735
		$right->setRow($right_row);
2736
		$right->setType($type);
2737
2738
		$elem_row = $this->getRowByPrefix($data, 'elem');
2739
		$elem = new absences_EntryElem();
2740
		$elem->setRow($elem_row);
2741
		$elem->setRight($right);
2742
		$elem->setEntry($this->entry);
2743
2744
		return $elem;
2745
	}
2746
2747
2748
2749
	public function executeQuery()
2750
	{
2751
		if(is_null($this->_oResult))
2752
		{
2753
			global $babDB;
2754
			$req = 'SELECT
2755
2756
				ee.id 					elem__id,
2757
				ee.id_entry 			elem__id_entry,
2758
				ee.id_right				elem__id_right,
2759
				ee.quantity				elem__quantity,
2760
			    ee.date_begin           elem__date_begin,
2761
			    ee.date_end             elem__date_end,
2762
2763
				r.id					right__id,
2764
				r.id_creditor			right__id_creditor,
2765
				r.kind					right__kind,
2766
			    r.createdOn			    right__createdOn,
2767
				r.date_entry			right__date_entry,
2768
				r.date_begin			right__date_begin,
2769
				r.date_end				right__date_end,
2770
				r.quantity				right__quantity,
2771
				r.quantity_unit			right__quantity_unit,
2772
				r.quantity_inc_month 	right__quantity_inc_month,
2773
				r.quantity_inc_max 		right__quantity_inc_max,
2774
				r.quantity_inc_last		right__quantity_inc_last,
2775
				r.id_type				right__id_type,
2776
				r.description			right__description,
2777
				r.active				right__active,
2778
				r.cbalance	 			right__cbalance,
2779
				r.date_begin_valid		right__date_begin_valid,
2780
				r.date_end_valid		right__date_end_valid,
2781
				r.date_end_fixed		right__date_end_fixed,
2782
				r.date_begin_fixed		right__date_begin_fixed,
2783
				r.no_distribution 		right__no_distribution,
2784
				r.use_in_cet			right__use_in_cet,
2785
				r.id_rgroup				right__id_rgroup,
2786
				r.earlier				right__earlier,
2787
				r.earlier_begin_valid 	right__earlier_begin_valid,
2788
				r.earlier_end_valid		right__earlier_end_valid,
2789
				r.later					right__later,
2790
				r.later_begin_valid		right__later_begin_valid,
2791
				r.later_end_valid		right__later_end_valid,
2792
				r.delay_before			right__delay_before,
2793
				r.id_report_type		right__id_report_type,
2794
				r.date_end_report		right__date_end_report,
2795
				r.description_report	right__description_report,
2796
				r.id_reported_from		right__id_reported_from,
2797
				r.quantity_alert_days	right__quantity_alert_days,
2798
				r.quantity_alert_types	right__quantity_alert_types,
2799
				r.quantity_alert_begin	right__quantity_alert_begin,
2800
				r.quantity_alert_end	right__quantity_alert_end,
2801
				r.dynconf_types			right__dynconf_types,
2802
				r.dynconf_begin			right__dynconf_begin,
2803
				r.dynconf_end			right__dynconf_end,
2804
				r.sync_status			right__sync_status,
2805
				r.sync_update			right__sync_update,
2806
				r.uuid					right__uuid,
2807
				r.archived				right__archived,
2808
				r.sortkey				right__sortkey,
2809
				r.require_approval		right__require_approval,
2810
2811
2812
				t.id					type__id,
2813
				t.name					type__name,
2814
				t.description			type__description,
2815
				t.color					type__color
2816
			FROM
2817
				absences_entries_elem ee,
2818
				absences_rights r,
2819
				absences_types t
2820
2821
			WHERE
2822
				ee.id_entry='.$babDB->quote($this->entry->id).'
2823
				AND ee.id_right = r.id
2824
				AND r.id_type = t.id';
2825
2826
			if (isset($this->types)) {
2827
			    $req .= ' AND t.id IN('.$babDB->quote($this->types).')';
2828
			}
2829
2830
		    $req .= ' ORDER BY ee.date_begin';
2831
2832
			$this->setMySqlResult($this->getDataBaseAdapter()->db_query($req));
2833
		}
2834
	}
2835
2836
2837
}
2838
2839
2840
2841
/**
2842
 * Entry periods iterator
2843
 */
2844
class absences_EntryPeriodIterator extends absences_Iterator
2845
{
2846
    /**
2847
     *
2848
     * @var absences_Entry
2849
     */
2850
    public $entry;
2851
2852
2853
2854
    public function getObject($data)
2855
    {
2856
        require_once dirname(__FILE__).'/entry_period.class.php';
2857
2858
2859
2860
        $elem_row = $this->getRowByPrefix($data, 'period');
2861
        $elem = new absences_EntryPeriod();
2862
        $elem->setRow($elem_row);
2863
        $elem->setEntry($this->entry);
2864
2865
        return $elem;
2866
    }
2867
2868
2869
2870
    public function executeQuery()
2871 11
    {
2872
        if(is_null($this->_oResult))
2873 11
        {
2874 11
            global $babDB;
2875 11
            $req = 'SELECT
2876
2877
				ep.id 					period__id,
2878
				ep.id_entry 			period__id_entry,
2879
			    ep.date_begin           period__date_begin,
2880
			    ep.date_end             period__date_end
2881
2882
			FROM
2883
				absences_entries_periods ep
2884
			WHERE
2885
				ep.id_entry='.$babDB->quote($this->entry->id).'';
2886 11
2887
2888
            $req .= ' ORDER BY ep.date_begin';
2889 11
2890
            $this->setMySqlResult($this->getDataBaseAdapter()->db_query($req));
2891 11
        }
2892 11
    }
2893 11
2894
2895
}
2896