Completed
Push — master ( 008ba1...b7a945 )
by Paul
10:08
created

absences_Entry::getYear()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 11
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 11
loc 11
ccs 0
cts 3
cp 0
crap 6
rs 9.4285
c 0
b 0
f 0
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
639
	/**
640 29
	 * Add dates to loaded elements using the user calendar
641
	 */
642 29
	public function setElementsDates()
643
	{
644 29
	    include_once $GLOBALS['babInstallPath']."utilit/dateTime.php";
645
646
	    if (0 === count($this->elements)) {
647
	        throw new absences_EntryException('No elements to set dates on, id_entry='.$this->id.', owner='.$this->getUserName());
648 29
	    }
649
	    
650
	    if ($this->checkElementsDates()) {
651 10
	        // dates allready set
652
	        // calling getFutureDate twice does not work
653
	        return;
654 29
	    }
655 6
656
	    if (1 === count($this->elements)) {
657 6
	        $element = reset($this->elements);
658 6
	        /*@var $element absences_EntryElem */
659 6
	        $element->date_begin = $this->date_begin;
660
	        $element->date_end = $this->date_end;
661
	        return;
662 23
	    }
663
	    
664 23
	    $this->sortElements();
665
        
666 23
	    $loop = BAB_DateTime::fromIsoDateTime($this->date_begin);
667
	    
668 23
	    
669
670 23
	    foreach($this->elements as $element) {
671 23
	        /*@var $element absences_EntryElem */
672
	        $element->date_begin = $loop->getIsoDateTime();
673 23
	        
674 2
	        $loop = $this->getFutureDate($loop, (float) $element->quantity, $element->getRight()->quantity_unit);
675 2
	        $element->date_end = $loop->getIsoDateTime();
676
	        
677 23
	        if ($element->date_end > $this->date_end) {
678 23
	            $element->date_end = $this->date_end;
679
	        }
680
	        
681 23
	        $loop = $this->getNextValidDate($loop);
682 21
	    }
683 21
	    
684 23
	    // round the last half day to the request period end
685
	    if ($this->getElementEndGap() <= 3600) {
686
	        $this->elements[count($this->elements)-1]->date_end = $this->date_end;
687
	    }
688
	}
689
	
690 23
	/**
691
	 * Get the gap beetween the last element end date and the period end date
692 23
	 * @return int
693 23
	 */
694
	protected function getElementEndGap()
695 23
	{
696 3
	    $computedLastDate = bab_mktime($this->elements[count($this->elements)-1]->date_end);
697
	    $periodEnd = bab_mktime($this->date_end);
698
	     
699 20
	    if ($periodEnd > $computedLastDate) {
700
	         return ($periodEnd - $computedLastDate);
701
	    }
702
	    
703 20
	    if ($computedLastDate > $periodEnd) {
704
	        return ($computedLastDate - $periodEnd);
705
	    }
706
	    
707
	    return 0;
708
	}
709
710
711
	/**
712 23
	 * set the date to the next valid date
713
	 * @param BAB_DateTime $date
714 23
	 * @return BAB_DateTime
715
	 */
716 23
	protected function getNextValidDate(BAB_DateTime $date)
717
	{
718 23
	    $moment = $date->getTimeStamp();
719 23
720
	    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...
721
722
	        if ($moment >= $period->ts_end) {
723
	            continue;
724 23
	        }
725 10
726
	        // if the future date (end date of the element) is in a worked period
727
	        // the future date is valid as a next date
728
	        if ($moment < $period->ts_end && $moment > $period->ts_begin) {
729 13
	            return $date;
730 21
	        }
731
732
733 21
	        return BAB_DateTime::fromTimeStamp($period->ts_begin);
734
	    }
735
736
737
	    return $date;
738
	}
739
740
741
742
	/**
743
	 * Add quantity to startdate only on working periods
744
	 *
745
	 * @param BAB_DateTime $startdate
746
	 * @param float $quantity
747 23
	 * @param string $quantity_unit D|H
748
	 *
749 23
	 * @return BAB_DateTime
750 4
	 */
751
	protected function getFutureDate(BAB_DateTime $startdate, $quantity, $quantity_unit)
752
	{
753 20
	    if ('H' === $quantity_unit) {
754
	        return $this->getFutureDate_Hours($startdate, $quantity);
755
	    }
756
757
	    return $this->getFutureDate_Days($startdate, $quantity);
758
	}
759
760
761 18
	/**
762
	 * Split working periods
763 18
	 * @return array
764 18
	 */
765
	protected function getWorkingPeriodsFromDate(BAB_DateTime $startdate)
766 18
	{
767
	    $periods = array();
768 18
	    $start = $startdate->getTimeStamp();
769
770 15
	    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...
771
772
	        if ($start >= $period->ts_end) {
773 18
	            // continue to the next worked period
774 10
	            continue;
775 10
	        }
776 10
777
	        if ($start > $period->ts_begin && $start < $period->ts_end) {
778
	            $period->setBeginDate($startdate);
779 18
	            $periods[] = $period;
780 18
	            continue;
781
	        }
782 18
783
	        $periods[] = $period;
784
	    }
785
786
	    return $periods;
787
	}
788
789
790
791
	/**
792
	 * Add quantity to startdate only on working periods
793
	 *
794
	 * @param BAB_DateTime $startdate
795 18
	 * @param float $seconds_to_add
796
	 *
797 18
	 * @return BAB_DateTime
798
	 */
799
	protected function getFutureDate_Seconds(BAB_DateTime $startdate, $seconds_to_add)
800 18
	{
801
	    $worked_total = 0; //seconds
802 18
803 18
804
	    foreach ($this->getWorkingPeriodsFromDate($startdate) as $period) {
805 18
806
	        $add_in_period = ($seconds_to_add - $worked_total);
807
	        $worked_total += $period->getDuration();
808
809 17
	        if ((int) $worked_total === (int) $seconds_to_add) {
810
	            // la duree de la periode de travail est egale a duree demandee
811
	            // en tenant compte des periodes de travail precedentes
812
813 15
	            return BAB_DateTime::fromTimeStamp($period->ts_end);
814
	        }
815 10
816 10
817
            if ($worked_total > $seconds_to_add) {
818
                // la date future se trouve a l'interieur d'une periode travaillee
819
                $futureDate = $period->ts_begin + $add_in_period;
820
                return BAB_DateTime::fromTimeStamp($futureDate);
821 15
            }
822
823 2
            // continue to the next worked period
824
825
	    }
826
827
	    return BAB_DateTime::fromIsoDateTime($this->date_end);
828
	}
829
830
831
832
833
834
835
	/**
836
	 * Add quantity to startdate only on working periods
837
	 *
838
	 * @param BAB_DateTime $startdate
839 4
	 * @param float $quantity  hours
840
	 *
841 4
	 * @return BAB_DateTime
842
	 */
843 4
	protected function getFutureDate_Hours(BAB_DateTime $startdate, $quantity)
844
	{
845
	    $seconds_to_add = $quantity * 3600;
846
847
	    return $this->getFutureDate_Seconds($startdate, $seconds_to_add);
848
	}
849
	
850
	
851
	
852
	
853
	
854
	
855
	
856
	
857 20
	
858
    
859 20
	
860 20
	
861 20
	
862
	/**
863
	 * Add quantity to startdate only on working periods
864 20
	 *
865 15
	 * @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...
866
	 * @param float $quantity  days
867
	 *
868
	 * @return BAB_DateTime
869
	 */
870 20
	protected function getFutureDate_Days(BAB_DateTime $startDate, $quantity)
871
	{
872 20
	    $total_seconds = $this->getTotalHours() * 3600;
873
	    $total_days = $this->getTotalDays();
874 20
	    $seconds_to_add = $quantity * $total_seconds / $total_days;
875 20
	    
876 20
	    return $this->getFutureDate_Seconds($startDate, $seconds_to_add);
877
	}
878 20
	
879 20
	
880
	
881
	
882 20
883
884 20
885 20
	/**
886 20
	 * Create the planned periods in the entry boundaries
887
	 * from the calendar working periods
888
	 */
889 20
	public function createPlannedPeriods()
890
	{
891 20
	    require_once dirname(__FILE__).'/entry_period.class.php';
892 19
	    $this->plannedPeriods = array();
893 19
894
	    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...
895 20
896
	        /*@var $workingPeriod bab_CalendarPeriod */
897
898
	        $plannedPeriod = new absences_EntryPeriod();
899 20
	        $plannedPeriod->setEntry($this);
900 20
	        $plannedPeriod->date_begin = date('Y-m-d H:i:s', $workingPeriod->ts_begin);
901 20
	        $plannedPeriod->date_end = date('Y-m-d H:i:s', $workingPeriod->ts_end);
902 20
903
	        $this->plannedPeriods[] = $plannedPeriod;
904
	    }
905
	}
906 20
907
908
	
909
910
911
912
	/**
913
	 * Save elements of entry to database
914
	 *
915
	 */
916
	public function saveElements()
917
	{
918
		$processed_ids = array();
919
920
		foreach ($this->elements as $elem)
921
		{
922
			/*@var $elem absences_EntryElem */
923
		    
924 20
		    try {
925
		    
926
    			$elem->save();
927 20
    			$processed_ids[] = $elem->id;
928
    			
929 20
		    } catch(Exception $e) {
930 20
		        // fail to save one element, it will be deleted or not created
931
		        bab_debug($e->getMessage());
932 20
		    }
933
		}
934
935 20
		// delete removed elements
936
937
		global $babDB;
938
939
		$babDB->db_query('DELETE FROM absences_entries_elem WHERE id_entry='.$babDB->quote($this->id).' AND id NOT IN('.$babDB->quote($processed_ids).')');
940 20
941 20
	}
942 20
943 20
944
945 20
946 20
	public function savePlannedPeriods()
947
	{
948 15
	    if (empty($this->plannedPeriods)) {
949
	        throw new Exception('Planned periods where not loaded or set');
950 15
	    }
951
952 15
	    $processed_ids = array();
953
954 15
	    foreach ($this->plannedPeriods as $period)
955 15
	    {
956
	        /*@var $elem absences_EntryPeriod */
957 15
	        $period->save();
958 9
959
	        $processed_ids[] = $period->id;
960 9
	    }
961 12
962
	    global $babDB;
963
964 15
	    $babDB->db_query('DELETE FROM absences_entries_periods WHERE id_entry='.$babDB->quote($this->id).' AND id NOT IN('.$babDB->quote($processed_ids).')');
965
966 15
	}
967
968
969 9
970 15
	/**
971
	 * Apply dynamic rights for all right involved in the entry
972
	 */
973
	public function applyDynamicRight()
974
	{
975
		require_once dirname(__FILE__).'/agent_right.class.php';
976 15
977
		$I = new absences_AgentRightManagerIterator();
978
		$I->setAgent($this->getAgent());
979
980
		foreach ($I as $agentRight)
981
		{
982
			/*@var $agentRight absences_AgentRight */
983 7
			$agentRight->applyDynamicRight();
984 7
		}
985 6
	}
986 6
987
988 7
989 7
990
	/**
991
	 * Add element to list
992
	 * @param absences_EntryElem $elem
993 7
	 */
994
	public function addElement(absences_EntryElem $elem)
995
	{
996
		$this->elements[] = $elem;
997
	}
998 8
999
	/**
1000
	 * Remove an element in the list
1001
	 * @param int $id_right
1002
	 *
1003
	 * @return bool
1004
	 */
1005 15
	public function removeElement($id_right)
1006 15
	{
1007 15
		foreach ($this->elements as $key => $elem)
1008 15
		{
1009 15
			if ($elem->id_right == $id_right)
1010
			{
1011 3
				unset($this->elements[$key]);
1012 3
				return true;
1013 15
			}
1014
		}
1015
1016
		return false;
1017 20
	}
1018 6
1019
1020
	/**
1021 15
	 * Set the working period index for the entry
1022
	 * Used in unit tests to set differents working periods
1023
	 * if not used, the workingPeriodIndex will be generated from the calendar API
1024 15
	 *
1025
	 */
1026
	public function setWorkingPeriodIndex(Array $index_working)
1027
	{
1028
	    $this->workingPeriodIndex = $index_working;
1029 15
	    return $this;
1030
	}
1031 15
1032
	/**
1033
	 * Get working period index for the user and for the period
1034
	 * @return array
1035
	 */
1036
	private function getWorkingPeriodIndex()
1037
	{
1038
	    if (!isset($this->workingPeriodIndex)) {
1039
1040
	        include_once $GLOBALS['babInstallPath']."utilit/dateTime.php";
1041 15
1042
	        list($index_working,, $is_free) = absences_getHalfDaysIndex(
1043 15
	            $this->id_user,
1044
	            BAB_DateTime::fromIsoDateTime($this->date_begin),
1045
	            BAB_DateTime::fromIsoDateTime($this->date_end),
1046
	            true
1047
	        );
1048
1049 15
1050
	        foreach ($index_working as $key => $period_list) {
1051 15
	            if (!isset($is_free[$key])) {
1052
	                unset($index_working[$key]);
1053 15
	            }
1054
	        }
1055
1056
	        $this->workingPeriodIndex = $index_working;
1057
	    }
1058
1059
	    return $this->workingPeriodIndex;
1060 15
	}
1061 1
1062 1
1063
1064
	/**
1065 14
	 * Number of free days and hours between two dates
1066 14
	 *
1067 15
	 */
1068
	private function loadDurations() {
1069 15
1070
		$this->duration_days 	= 0.0;
1071
		$this->duration_hours 	= 0.0;
1072
		$this->working_periods = array();
1073
1074
1075
1076
		$index_working = $this->getWorkingPeriodIndex();
1077
1078
1079
		foreach ($index_working as $key => $period_list) {
1080
1081
			$this->duration_days += 0.5;
1082
1083
			foreach($period_list as $p)
1084
			{
1085
				/*@var $p bab_CalendarPeriod */
1086
1087
				if ($p->getCollection() instanceof bab_WorkingPeriodCollection)
1088
				{
1089
				    $this->working_periods[] = $p;
1090
					$this->duration_hours 	+= ($p->getDuration() / 3600);
1091
				}
1092
			}
1093
		}
1094
	}
1095
1096
1097
	/**
1098
	 * Get list of working periods of the entry
1099
	 * @return bab_CalendarPeriod[]
1100
	 */
1101
	protected function getWorkingPeriods()
1102
	{
1103
	    if (!isset($this->working_periods)) {
1104
	        $this->loadDurations();
1105
	    }
1106
1107
	    return $this->working_periods;
1108
	}
1109
1110
1111
	/**
1112
	 * List of working periods for one day
1113
	 * @param string $date 10 chars
1114
	 *
1115
	 * @return bab_CalendarPeriod[]
1116
	 */
1117
	public function getDayWorkingPeriods($date)
1118
	{
1119
	    $return = array();
1120
	    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...
1121
	        if ($date === date('Y-m-d', $period->ts_begin)) {
1122
	            $return[] = $period;
1123
	        }
1124
	    }
1125
1126
	    return $return;
1127
	}
1128
1129
1130
	/**
1131
	 * List of working periods for one day
1132
	 * @param string $date 10 chars
1133
	 *
1134
	 * @return absences_EntryPeriod[]
1135 23
	 */
1136
	public function getDayPlannedPeriods($date)
1137 23
	{
1138 23
	    $return = array();
1139
	    foreach ($this->getPlannedPeriodsIterator() as $period) {
1140 23
	        if ($date === substr($period->date_begin, 0, 10)) {
1141
	            $return[] = $period;
1142
	        }
1143
	    }
1144 23
1145 23
	    return $return;
1146 23
	}
1147 23
1148
1149 23
1150 23
1151 23
	/**
1152
	 * Get period duration in days
1153
	 * @return float
1154
	 */
1155
	public function getDurationDays()
1156
	{
1157
		if (!isset($this->duration_days))
1158
		{
1159
			$this->loadDurations();
1160
		}
1161
1162
		return $this->duration_days;
1163
	}
1164
1165
	/**
1166
	 * Get period duration in hours
1167
	 * @return float
1168
	 */
1169
	public function getDurationHours()
1170
	{
1171
		if (!isset($this->duration_hours))
1172
		{
1173
			$this->loadDurations();
1174
		}
1175
1176
		return $this->duration_hours;
1177
	}
1178
1179
1180
	/**
1181
	 * Convert a number of days to hours
1182
	 * @param float $days
1183
	 * @return float
1184
	 */
1185
	private function daysToHours($days)
1186
	{
1187
		$ratio = $this->getPlannedDurationHours() / $this->getPlannedDurationDays();
1188
		return round(($ratio * $days), 2);
1189
	}
1190
1191
	/**
1192
	 * Convert a number of hours to days
1193
	 * @param float $hours
1194
	 * @return float
1195
	 */
1196
	private function hoursToDays($hours)
1197
	{
1198
		$ratio = $this->getPlannedDurationDays() / $this->getPlannedDurationHours();
1199
		return round(($ratio * $hours), 2);
1200
	}
1201
1202
1203
1204
	/**
1205
	 * Compute totals for loaded elements
1206
	 */
1207
	private function loadedElementsTotal()
1208
	{
1209
		if (empty($this->elements))
1210
		{
1211
			$this->loadElements();
1212
		}
1213
1214
		$this->total_days = 0.0;
1215
		$this->total_hours = 0.0;
1216
		$this->total_type_days = array();
1217
		$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...
1218
1219
		foreach($this->elements as $elem)
1220
		{
1221
			/*@var $elem absences_EntryElem */
1222
			$right = $elem->getRight();
1223
1224
			$quantity = (float) $elem->quantity;
1225
1226
			switch($right->quantity_unit)
1227
			{
1228
				case 'D':
1229
					$hours = $this->daysToHours($quantity);
1230
					$this->addQUantityInCache($right, $quantity, $hours);
1231
					break;
1232 20
				case 'H':
1233
				    $days = $this->hoursToDays($quantity);
1234 20
				    $this->addQUantityInCache($right, $days, $quantity);
1235 20
					break;
1236
			}
1237 20
		}
1238 14
	}
1239
1240
1241 20
	/**
1242 6
	 * Populate the cache variables for one element
1243
	 *
1244
	 * @param absences_Right $right
1245 20
	 * @param float $days
1246 20
	 * @param float $hours
1247
	 */
1248
	private function addQUantityInCache(absences_Right $right, $days, $hours)
1249
	{
1250
	    if (!isset($this->total_type_days[$right->id_type])) {
1251
	        $this->total_type_days[$right->id_type] = 0.0;
1252
	    }
1253
1254
	    if (!isset($this->total_type_hours[$right->id_type])) {
1255
	        $this->total_type_hours[$right->id_type] = 0.0;
1256
	    }
1257
1258
	    $this->total_days += $days;
1259
	    $this->total_hours += $hours;
1260
	    $this->total_type_days[$right->id_type] += $days;
1261
	    $this->total_type_hours[$right->id_type] += $hours;
1262
	}
1263
1264
1265
1266
1267
1268
	/**
1269
	 * Get sum of elements quantity, converted in days
1270
	 *
1271
	 * @param int $id_type Optional filter by type
1272
	 *
1273
	 * @return float
1274
	 */
1275 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...
1276
	{
1277
		if (!isset($this->total_days)) {
1278
			$this->loadedElementsTotal();
1279
		}
1280
1281
		if (isset($id_type)) {
1282
1283
		    if (!isset($this->total_type_days[$id_type])) {
1284
		        return 0.0;
1285
		    }
1286
1287
		    return $this->total_type_days[$id_type];
1288
		}
1289
1290
		return $this->total_days;
1291
	}
1292
1293
	/**
1294
	 * Get sum of elements quantity, converted in hours
1295
	 *
1296
	 * @param int $id_type Optional filter by type
1297
	 *
1298
	 * @return float
1299
	 */
1300 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...
1301
	{
1302
		if (!isset($this->total_hours)) {
1303
			$this->loadedElementsTotal();
1304
		}
1305
1306
		if (isset($id_type)) {
1307
1308
		    if (!isset($this->total_type_hours[$id_type])) {
1309
		        return 0.0;
1310
		    }
1311
1312
		    return $this->total_type_hours[$id_type];
1313
		}
1314
1315
		return $this->total_hours;
1316
	}
1317
1318
1319
1320
1321
	/**
1322
	 * Number of days on entry between two dates
1323
	 * si les dates de l'element sont a cheval sur la periode demandee
1324
	 * on utlise les heures travailles qui etait en vigeur au moment de la creation
1325
	 * de la demande
1326
	 *
1327
	 * @param string   $begin	Datetime
1328
	 * @param string   $end		Datetime
1329
	 * @param int      $id_type Optional filter by type
1330
	 *
1331
	 * @return float (days)
1332
	 */
1333 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...
1334
	{
1335
	    if (empty($this->elements))
1336
	    {
1337 39
	        $this->loadElements();
1338
	    }
1339 39
1340 39
	    $total = 0.0;
1341
	    foreach($this->elements as $elem)
1342
	    {
1343
	        /*@var $elem absences_EntryElem */
1344
	        $right = $elem->getRight();
1345
1346
	        if (isset($id_type) && (int) $id_type !== (int) $right->id_type) {
1347
	            continue;
1348
	        }
1349
1350
	        $quantity = $elem->getPlannedQuantityBetween($begin, $end);
1351
1352
	        if ('H' === $right->quantity_unit) {
1353
	            $days = $this->hoursToDays($quantity);
1354
	        } else {
1355
	            $days = $quantity;
1356
	        }
1357
1358
	        $total += $days;
1359
	    }
1360
1361
	    return $total;
1362
	}
1363
1364
1365
1366
1367
	/**
1368
	 * Number of hours on entry between two dates
1369 38
	 * si les dates de l'element sont a cheval sur la periode demandee
1370
	 * on utlise les heures travailles qui etait en vigeur au moment de la creation
1371 38
	 * de la demande
1372 38
	 *
1373
	 * @param string   $begin	Datetime
1374
	 * @param string   $end		Datetime
1375
	 * @param int      $id_type Optional filter by type
1376
	 *
1377
	 * @return float (hours)
1378
	 */
1379 35 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...
1380
	{
1381 35
	    if (empty($this->elements))
1382
	    {
1383
	        $this->loadElements();
1384
	    }
1385
1386
	    $total = 0.0;
1387
	    foreach($this->elements as $elem)
1388
	    {
1389
	        /*@var $elem absences_EntryElem */
1390
	        $right = $elem->getRight();
1391
1392
	        if (isset($id_type) && (int) $id_type !== (int) $right->id_type) {
1393
	            continue;
1394
	        }
1395
1396
	        $quantity = $elem->getPlannedQuantityBetween($begin, $end);
1397
1398
	        if ('D' === $right->quantity_unit) {
1399
	            $hours = $this->daysToHours($quantity);
1400
	        } else {
1401
	            $hours = $quantity;
1402 35
	        }
1403
1404
	        $total += $hours;
1405
	    }
1406
1407
	    return $total;
1408
	}
1409
1410
1411 35
1412
1413 35
1414 35
1415 35
1416
1417
	/**
1418
	 * Test if the loaded elements in entry contains rights in hours
1419 35
	 * @return bool
1420
	 */
1421
	public function containsHours()
1422 35
	{
1423
		foreach($this->elements as $elem)
1424 35
		{
1425
			if ('H' === $elem->getRight()->quantity_unit)
1426 35
			{
1427
				return true;
1428
			}
1429
		}
1430 35
1431 35
		return false;
1432 35
	}
1433 35
1434 35
1435 35
	/**
1436 35
	 * Test if loaded elements in entry require approval
1437 35
	 *
1438
	 * @return bool
1439
	 */
1440
	public function requireApproval()
1441
	{
1442
		if (empty($this->elements))
1443
		{
1444 35
			$this->loadElements();
1445
		}
1446 35
1447 35
		foreach($this->elements as $elem)
1448 35
		{
1449
			if (1 === (int) $elem->getRight()->require_approval)
1450 35
			{
1451
				return true;
1452
			}
1453
		}
1454
1455
		return false;
1456
	}
1457
	
1458
	
1459
	public function checkAvailable()
1460
	{
1461
	    $res = $this->getElementsIterator();
1462
	    foreach ($res as $element) {
1463
	        /*@var $element absences_EntryElem */
1464
	        if (!$element->isQuantityAvailable()) {
1465
	            throw new Exception(sprintf(absences_translate('Failed to submit this request, the quantity for right %s s not available'), $element->getRight()->description));
1466
	        }
1467
	    }
1468
	}
1469
1470
	
1471
	/**
1472
	 * Check elements validity
1473
	 * @throws UnexpectedValueException
1474
	 * 
1475
	 * @return int number of elements with quantity > 0
1476
	 */
1477
	protected function checkElementsValidity()
1478
	{
1479
	    $count = 0;
1480
	    foreach($this->elements as $elem)
1481
	    {
1482
	        $quantity = (int) round(100 * $elem->quantity);
1483
	        if ($quantity > 0)
1484
	        {
1485
	            $count++;
1486
	        }
1487
	    
1488
	        $elem->checkValidity();
1489
	    }
1490
	    
1491
	    return $count;
1492
	}
1493
	
1494
	
1495
	/**
1496
	 * throw an exception if there is another entry in the same period
1497
	 * @throws absences_EntryException
1498
	 */
1499
	protected function checkOtherEntriesValidity()
1500
	{
1501
	    $otherEntries = new absences_EntryIterator();
1502
	    $otherEntries->from = $this->date_begin;
1503
	    $otherEntries->to = $this->date_end;
1504
	    $otherEntries->users = array($this->id_user);
1505
	    
1506
	    
1507
	    foreach ($otherEntries as $otherEntry) {
1508
	    
1509
	        /* @var $otherEntry absences_Entry */
1510
	    
1511 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...
1512
	            $e = new absences_EntryException(absences_translate('There is allready an absence request in the period'));
1513
	            $e->entry = $this;
1514
	            $e->blocking = true;
1515
	            throw $e;
1516
	        }
1517
	    }
1518
	    
1519
	}
1520
1521
1522
	/**
1523
	 * Check request validity
1524
	 *
1525
	 * @throws absences_EntryException
1526
	 * @throws UnexpectedValueException
1527
	 * 
1528
	 * @return bool
1529
	 */
1530
	public function checkValidity()
1531
	{
1532
		// verify mandatory data
1533
1534
		if (!isset($this->id_user) || $this->id_user <= 0)
1535
		{
1536
			throw new UnexpectedValueException('Unexpected id_user');
1537
		}
1538
1539
1540
		$count = $this->checkElementsValidity();
1541
1542
1543 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...
1544
		{
1545
			$e = new absences_EntryException(absences_translate('At least one vacation right must be used'));
1546
			$e->entry = $this;
1547
			$e->blocking = true;
1548
			throw $e;
1549
		}
1550
1551
1552
1553
		// test user validity
1554
1555
		require_once $GLOBALS['babInstallPath'].'utilit/userinfosincl.php';
1556
1557
		$creationdate = bab_userInfos::getCreationDate($this->id_user);
1558
1559
		if ($creationdate >= $this->date_end) {
1560
			$e = new absences_EntryException(sprintf(absences_translate('The vacation request end before user creation date : %s'), bab_shortDate(bab_mktime($creationdate))));
1561
			$e->entry = $this;
1562
			$e->blocking = true;
1563
			throw $e;
1564
		}
1565
1566
		// T8359 : si l'utilisateur est reactive la demande ne sera pas presente, il faudra reenregistrer le droit
1567
		// avant la correction de ce ticket, on creeais les demandes pour eviter ce probleme
1568
1569
		if (!bab_userInfos::isValid($this->id_user)) {
1570
1571
		    /*
1572
		    $e = new absences_EntryException(absences_translate('This user account is disabled'));
1573
		    $e->entry = $this;
1574
		    $e->blocking = true;
1575
		    throw $e;
1576
		    */
1577
1578
		    return false;
1579
		}
1580
1581
1582
		// test quantity / period duration in days
1583
1584
		$total = (int) round(100 * $this->getTotalDays());
1585
		$duration = (int) round(100 * $this->getDurationDays());
1586
1587
1588 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...
1589
		{
1590
			$e = new absences_EntryException(absences_translate('The selected period is not available'));
1591
			$e->entry = $this;
1592
			$e->blocking = true;
1593
			throw $e;
1594
		}
1595
1596
1597
1598
		if ($total !== $duration)
1599
		{
1600
			$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')));
1601
			$e->entry = $this;
1602
			$e->blocking = !((bool) absences_getVacationOption('allow_mismatch'));
1603
			throw $e;
1604
		}
1605
1606
		// test quantity / period duration in hours only if there is one right in hours
1607
		/*
1608
		 *
1609
		 * 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
1610
		 * 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)
1611
		 *
1612
		if ($this->containsHours())
1613
		{
1614
			$total = (int) round(100 * $this->getTotalHours());
1615
			$duration = (int) round(100 * $this->getDurationHours());
1616
1617
			if ($total !== $duration)
1618
			{
1619
				$e = new absences_EntryException(absences_translate('The total quantity does not match the period duration in hours'));
1620
				$e->blocking = !((bool) absences_getVacationOption('allow_mismatch'));
1621
				throw $e;
1622
			}
1623
		}
1624
1625
		*/
1626
1627
1628
1629
		// verifier si la periode demandee n'est pas deja occuppe par une autre demande
1630
        $this->checkOtherEntriesValidity();
1631
		
1632
1633
		return true;
1634
	}
1635
1636
1637
	/**
1638
	 * (non-PHPdoc)
1639
	 * @see absences_Request::modifiedOn()
1640
	 *
1641
	 * @return string
1642
	 */
1643
	public function modifiedOn()
1644
	{
1645
		return $this->date;
1646
	}
1647
1648
	/**
1649
	 * Test if the entry is a fixed vacation
1650
	 * @return bool
1651
	 */
1652
	public function isFixed()
1653
	{
1654
		return (1 === (int) $this->creation_type);
1655
1656
	}
1657
1658
1659
1660
1661
1662
	/**
1663
	 * Test if entry is previsonal
1664
	 * @return bool
1665
	 */
1666
	public function isPrevisonal()
1667
	{
1668
		return ($this->status === 'P');
1669
	}
1670
1671
	/**
1672
	 * Test if at least one entry in folder is previsonal
1673
	 * @return boolean
1674
	 */
1675
	public function isFolderPrevisonal()
1676
	{
1677
		if (!$this->folder)
1678
		{
1679
			return false;
1680
		}
1681
1682
		$I = new absences_EntryIterator;
1683
		$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...
1684
		$I->status = 'P';
1685
1686
		return ($I->count() > 0);
1687
	}
1688 1
1689
	/**
1690 1
	 * Get the first entry in folder
1691 1
	 * @return absences_Entry
1692
	 */
1693 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...
1694
	{
1695 1
		if (!$this->folder)
1696 1
		{
1697
			return null;
1698
		}
1699 1
1700
		$I = new absences_EntryIterator;
1701 1
		$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...
1702
1703
		foreach($I as $first)
1704
		{
1705 1
			return $first;
1706
		}
1707 1
1708
		return null;
1709
	}
1710 1
1711
1712
	/**
1713 1
	 * Process specific code when the request is rejected
1714 1
	 *
1715
	 */
1716 1
	protected function onReject()
1717
	{
1718
		$this->updateCalendar();
1719
	}
1720
1721
1722
	/**
1723
	 * Process specific code when the request is confirmed
1724
	 *
1725
	 */
1726
	public function onConfirm()
1727
	{
1728
		$this->updateCalendar();
1729
	}
1730
1731
1732
1733
	/**
1734
	 * Call the user calendar backend to update the event
1735
	 * call the event to notify about calendar event modification
1736
	 */
1737
	public function updateCalendar()
1738
	{
1739
		require_once $GLOBALS['babInstallPath'].'utilit/dateTime.php';
1740
1741
		// try to update event copy in other backend (caldav)
1742
1743
		$begin = BAB_DateTime::fromIsoDateTime($this->date_begin);
1744
		$end = BAB_DateTime::fromIsoDateTime($this->date_end);
1745
		$period = absences_getPeriod($this->id, $this->id_user,  $begin, $end);
1746
		if ($period) {
1747
1748
			// probably set a new description if the event has been approved or rejected
1749
			absences_setPeriodProperties($period, $this);
1750
1751
			// save copy of event to calendar backend (if caldav)
1752
			$period->save();
1753
		}
1754
1755
1756
		// Update calendar data overlapped with event
1757
1758
		$date_begin = bab_mktime($this->date_begin);
1759
		$date_end	= bab_mktime($this->date_end);
1760
1761
		include_once $GLOBALS['babInstallPath']."utilit/eventperiod.php";
1762
		$event = new bab_eventPeriodModified($date_begin, $date_end, $this->id_user);
1763
		$event->types = BAB_PERIOD_VACATION;
1764
		bab_fireEvent($event);
1765
	}
1766
1767
1768
	public function getTitle()
1769
	{
1770
	    if (isset($this->todelete) && $this->todelete) {
1771
	        return absences_translate('vacation request to delete');
1772
	    }
1773
	    
1774
		return absences_translate('vacation request');
1775
	}
1776
1777
1778 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...
1779
	{
1780
		return array(
1781
			absences_translate('From') 		=> bab_shortDate(bab_mktime($this->date_begin)),
1782
			absences_translate('Until')		=> bab_shortDate(bab_mktime($this->date_end)),
1783
			absences_translate('Quantity') 	=> absences_quantity($this->getTotalDays(), 'D')
1784
		);
1785
	}
1786
1787
1788 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...
1789
	{
1790
		$year = (int) substr($this->date_begin, 0, 4);
1791
1792
		if (0 === $year)
1793
		{
1794
			return null;
1795
		}
1796
1797
		return $year;
1798
	}
1799
1800
1801 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...
1802
	{
1803
		require_once $GLOBALS['babInstallPath'].'utilit/dateTime.php';
1804
		$year = (int) substr($this->date_begin, 0, 4);
1805
1806
		if (0 === $year)
1807
		{
1808
			return null;
1809
		}
1810
1811
		$day = absences_getVacationOption('archivage_day');
1812
		$month = absences_getVacationOption('archivage_month');
1813
1814
		$currentYear = new BAB_DateTime($year, $month, $day);
1815
		if($this->date_begin < $currentYear->getIsoDate()){
1816
			$year = $year-1;
1817
		}
1818
1819
		return $year;
1820
	}
1821
1822 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...
1823
	{
1824
		global $babDB;
1825
1826
		$babDB->db_query("
1827
			UPDATE absences_entries
1828
			SET archived=".$babDB->quote(1)."
1829
			WHERE
1830
				id=".$babDB->quote($this->id)."
1831
		");
1832
	}
1833
1834
1835 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...
1836
	{
1837
		global $babDB;
1838
1839
		if (!$this->id)
1840
		{
1841
			throw new Exception('Missing ID');
1842
		}
1843
1844
		$babDB->db_query("
1845
			UPDATE absences_entries
1846
				SET appr_notified=".$babDB->quote(1)."
1847
			WHERE
1848
				id=".$babDB->quote($this->id)."
1849
		");
1850
	}
1851
1852
1853
	public function getManagerEditUrl()
1854
	{
1855
		$addon = bab_getAddonInfosInstance('absences');
1856
		$ts = bab_mktime($this->date_begin);
1857
		return $addon->getUrl().'vacuser&idx=period&id='.$this->id.'&year='.date('Y', $ts).'&month='.date('n', $ts).'&rfrom=1';
1858
	}
1859
1860
1861
	public function getManagerDeleteUrl()
1862
	{
1863
		$addon = bab_getAddonInfosInstance('absences');
1864
		$url = new bab_url($addon->getUrl().'vacadmb');
1865
		$url->idx = 'delete';
1866
		$url->id_entry = $this->id;
1867
		$url->from = $_SERVER['REQUEST_URI'];
1868
		return $url->toString();
1869
	}
1870
1871
1872
	public function getManagerFrame()
1873
	{
1874
		$W = bab_Widgets();
1875
		$vbox = $W->VBoxLayout();
1876
1877
		if ($this->isFixed())
1878
		{
1879
			$vbox->addItem($W->Icon(absences_translate('Fixed vacation period'), Func_Icons::ACTIONS_VIEW_CALENDAR_DAY));
1880
		} else {
1881
			$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));
1882
		}
1883
		$vbox->addItem($W->Label(absences_DateTimePeriod($this->date_begin, $this->date_end)));
1884
1885
		return $vbox;
1886
	}
1887
1888
1889
	/**
1890
	 *
1891
	 * @param bool 			$display_username	Affiche le nom du demandeur dans la card frame ou non, utile pour les listes contenant plusieurs demandeurs possibles
1892
	 * @param int			$rfrom				si les demandes de la liste sont modifiee par un gestionnaire ou gestionnaire delegue
1893
 	 * @param int			$ide				id entite de l'organigramme >0 si gestionnaire delegue seulement
1894
	 */
1895
	public function getCardFrame($display_username, $rfrom, $ide)
1896
	{
1897
		bab_functionality::includeOriginal('Icons');
1898
1899
		$W = bab_Widgets();
1900
		$layout = $W->HBoxLayout()->setHorizontalSpacing(2,'em')->addClass('widget-full-width');
1901
		$frame = $W->Frame(null, $layout);
1902
1903
		$frame->addClass(Func_Icons::ICON_LEFT_16);
1904
		$frame->addClass('absences-entry-cardframe');
1905
		$layout->addItem($col1 = $W->VBoxLayout()->setVerticalSpacing(.5,'em'));
1906
		$layout->addItem($col2 = $W->VBoxLayout()->setVerticalSpacing(.3,'em'));
1907
1908
		if ($this->isPrevisonal())
1909
		{
1910
			$btn = $W->Link($W->Button()->addItem($W->Label(absences_translate('Submit for approval'))), absences_addon()->getUrl()."vacuser&idx=subprev&id_entry=".$this->id);
1911
			$btn->setSizePolicy('display-opened');
1912
			$layout->addItem($btn);
1913
		}
1914
1915
		if ($this->isFolderPrevisonal())
1916
		{
1917
			$first = $this->getFolderFirst();
1918
			if ($first && $first->id === $this->id)
1919
			{
1920
				$btn = $W->Link($W->Button()->addItem($W->Label(absences_translate('Submit requests for approval'))), absences_addon()->getUrl()."vacuser&idx=subprev&id_entry=".$this->id);
1921
				$btn->setSizePolicy('display-closed');
1922
				$layout->addItem($btn);
1923
			}
1924
		}
1925
1926
1927
		$layout->addItem($col3 = $W->VBoxLayout()->setVerticalSpacing(.5,'em'));
1928
1929
		if ($this->isFixed())
1930
		{
1931
			$col1->addItem($W->Icon(absences_translate('Fixed vacation period'), Func_Icons::ACTIONS_VIEW_CALENDAR_DAY));
1932
			$col1->setCanvasOptions($col1->Options()->width(25,'em'));
1933
		} else {
1934
			$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));
1935
			$col1->setCanvasOptions($col1->Options()->width(25,'em'));
1936
1937
			$col1->addItem($this->getStatusIcon());
1938
		}
1939
1940
		$col2->setSizePolicy(Widget_SizePolicy::MAXIMUM);
1941
1942
		$col2->addItem($W->Title(absences_DateTimePeriod($this->date_begin, $this->date_end), 5));
1943
1944
		if ($display_username)
1945
		{
1946
			$col2->addItem($this->labelledValue(absences_translate('Owner'), $this->getUserName()));
1947
		}
1948
1949
		$col2->addItem($this->labelledValue(absences_translate('Quantity'), absences_vacEntryQuantity($this->id)));
1950
1951
1952
1953
1954 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...
1955
		{
1956
			$col3->addItem($W->Link($W->Icon(absences_translate('Modify'), Func_Icons::ACTIONS_DOCUMENT_EDIT), $this->getEditUrl($rfrom, $ide)));
1957
		}
1958
1959
		if ($this->canDelete())
1960
		{
1961
			$urldelete = absences_addon()->getUrl()."vacuser&idx=delete&id_entry=".$this->id;
1962
			$col3->addItem($W->Link($W->Icon(absences_translate('Delete'), Func_Icons::ACTIONS_EDIT_DELETE), $urldelete));
1963
		}
1964
1965
		$frame->setTitle(sprintf(absences_translate('Created the %s'), bab_shortDate(bab_mktime($this->createdOn()))));
1966
1967
		return $frame;
1968
	}
1969
1970
1971
1972
1973
1974
	/**
1975
	 * @return string
1976
	 */
1977
	public function getEditUrl($rfrom, $ide = null)
1978
	{
1979
		$begin_ts = bab_mktime($this->date_begin);
1980
		$url = absences_addon()->getUrl()."vacuser&idx=period&id=".$this->id."&year=".date('Y',$begin_ts)."&month=".date('n',$begin_ts);
1981
1982
		if (isset($rfrom))
1983
		{
1984
			$url .= '&rfrom='.$rfrom;
1985
		}
1986
1987
		if (isset($ide))
1988
		{
1989
			$url .= '&ide='.$ide;
1990
		}
1991
1992
		return $url;
1993
	}
1994
1995
1996
1997
	/**
1998 1
	 * Delete the vacation request
1999
	 * @return bool
2000 1
	 */
2001
	public function delete()
2002
	{
2003
2004
2005
2006
		global $babDB;
2007
2008
		if (!$this->getRow())
2009
		{
2010
			return false;
2011
		}
2012
2013
		parent::delete();
2014
		
2015
		if ('0000-00-00 00:00:00' !== $this->date_begin && '0000-00-00 00:00:00' !== $this->date_end) {
2016
    		include_once $GLOBALS['babInstallPath']."utilit/dateTime.php";
2017
    		$date_begin = BAB_DateTime::fromIsoDateTime($this->date_begin);
2018
    		$date_end	= BAB_DateTime::fromIsoDateTime($this->date_end);
2019
    
2020
    		$period = absences_getPeriod($this->id, $this->id_user, $date_begin, $date_end);
2021
    		if (null !== $period)
2022
    		{
2023
    			$period->delete();
2024
    		}
2025
		}
2026
		$babDB->db_query("DELETE FROM absences_dynamic_rights WHERE id_entry=".$babDB->quote($this->id));
2027
		$babDB->db_query("DELETE FROM ".ABSENCES_ENTRIES_ELEM_TBL." WHERE id_entry=".$babDB->quote($this->id)."");
2028
		$babDB->db_query("DELETE FROM ".ABSENCES_ENTRIES_TBL." WHERE id=".$babDB->quote($this->id));
2029
2030
		$this->applyDynamicRight();
2031
		
2032
		if (isset($date_begin) && isset($date_end)) {
2033
    		$cal_begin = clone $date_begin;
2034
    		$cal_end = clone $date_end;
2035
    
2036
    		$cal_end->add(1, BAB_DATETIME_MONTH);
2037
    
2038
    		while ($cal_begin->getTimeStamp() <= $cal_end->getTimeStamp()) {
2039
    			$month	= $cal_begin->getMonth();
2040
    			$year	= $cal_begin->getYear();
2041
    			absences_updateCalendar($this->id_user, $year, $month);
2042
    			$cal_begin->add(1, BAB_DATETIME_MONTH);
2043
    		}
2044
    		
2045
    		$this->addMovement(
2046
    		        sprintf(
2047
    		                absences_translate('The vacation entry from %s to %s has been deleted'),
2048
    		                $date_begin->shortFormat(true),
2049
    		                $date_end->shortFormat(true)
2050
    		        )
2051
    		);
2052
		}
2053
2054
		
2055
2056
		return true;
2057
	}
2058
2059
2060
2061
2062
2063
2064
2065
	/**
2066
	 * (non-PHPdoc)
2067
	 * @see absences_Request::notifyOwner()
2068
	 */
2069
	public function notifyOwner()
2070
	{
2071
		parent::notifyOwner();
2072
2073
		if ('Y' != $this->status)
2074
		{
2075
			return;
2076
		}
2077
2078
		$agent = $this->getAgent();
2079
		$emails = $agent->getEmails();
2080
2081
		if (empty($emails))
2082
		{
2083
			return;
2084
		}
2085
2086
		require_once dirname(__FILE__).'/request.notify.php';
2087
		absences_notifyEntryOwnerEmails(array($this), $emails);
2088
	}
2089
2090
}
2091
2092
2093
2094
2095
class absences_EntryException extends Exception
2096
{
2097
	/**
2098
	 * Optional entry associated to error (unsaved entry object)
2099
	 * @var absences_Entry
2100
	 */
2101
	public $entry;
2102
2103
2104
	/**
2105
	 * Define if the exception if blocking a save
2106
	 * @var bool
2107
	 */
2108
	public $blocking = true;
2109
}
2110
2111
2112
2113
/**
2114
 * Vacation requests
2115
 * Sorted by creation date
2116
 */
2117
class absences_EntryIterator extends absences_Iterator
2118
{
2119
2120
	/**
2121
	 *
2122
	 * @var string
2123
	 */
2124
	public $orderby = 'createdOn ASC';
2125
2126
2127
	/**
2128
	 *
2129
	 * @var string
2130
	 */
2131
	public $folder;
2132
2133
	/**
2134
	 *
2135
	 * @var array
2136
	 */
2137
	public $users;
2138
2139
2140
	/**
2141
	 *
2142
	 * @var mixed (array|string)
2143
	 */
2144
	public $status;
2145
2146
2147
	/**
2148
	 * Entry to ignore in the folder
2149
	 * @var int
2150
	 */
2151
	public $id_entry_folder;
2152
2153
2154
	/**
2155
	 * Filtrer les demandes necessitant ou pas un email aux approbateurs
2156
	 * @var int
2157
	 */
2158
	public $appr_notified;
2159
2160
	/**
2161
	 * Filtrer les demandes avec unes instance d'approbation
2162
	 * @var bool
2163
	 */
2164
	public $idfai_set;
2165
2166
2167
	/**
2168
	 * Filtrer les demandes avec une liste d'instances d'approbation
2169
	 * @var array
2170
	 */
2171
	public $appr_idfai;
2172
2173
2174
	/**
2175
	 * Search all request created before this date time
2176
	 * @var string
2177
	 */
2178
	public $createdOn;
2179
2180
	/**
2181
	 * Search all request modified before this date
2182
	 * @var string
2183
	 */
2184
	public $modifiedOn;
2185
2186
2187
	/**
2188
	 * Datetime
2189
	 * @var string
2190
	 */
2191
	public $from;
2192
2193
2194
	/**
2195
	 * Datetime
2196
	 * @var string
2197
	 */
2198
	public $to;
2199
2200
2201
	/**
2202
	 * Filtrer les demandes par annee
2203
	 * @var int
2204
	 */
2205
	public $year;
2206
2207
2208
	/**
2209
	 * Filtrer les demandes par organisation
2210
	 * @var int
2211
	 */
2212
	public $organization;
2213
2214
2215
	/**
2216
	 * Filtrer les demandes par statut d'archivage
2217
	 * @var int
2218
	 */
2219
	public $archived = 0;
2220
	
2221
	/**
2222
	 * with at least one element with this right
2223
	 */
2224
	public $id_right;
2225
2226
2227
2228
	public function getObject($data)
2229
	{
2230
2231
		$entry = new absences_Entry;
2232
		$entry->setRow($data);
2233
		return $entry;
2234
2235
	}
2236
2237
2238
2239
	public function executeQuery()
2240
	{
2241
		if(is_null($this->_oResult))
2242
		{
2243
			global $babDB;
2244
			$req = 'SELECT e.* 
2245
				FROM
2246
					absences_entries e 
2247
				';
2248
2249
2250
			$where = array();
2251
			if (isset($this->folder))
2252
			{
2253
				$where[] = 'e.folder='.$babDB->quote($this->folder);
2254
2255
				if (isset($this->id_entry_folder))
2256
				{
2257
					$where[] = 'e.id<>'.$babDB->quote($this->id_entry_folder);
2258
				}
2259
			}
2260
2261
			if (!empty($this->users))
2262
			{
2263
				$where[] = 'e.id_user IN('.$babDB->quote($this->users).')';
2264
			}
2265
2266
			if (isset($this->status))
2267
			{
2268
				$where[] = 'e.status IN('.$babDB->quote($this->status).')';
2269
			}
2270
2271
			if (isset($this->createdOn))
2272
			{
2273
				$where[] = 'e.createdOn<='.$babDB->quote($this->createdOn);
2274
			}
2275
2276
			if (isset($this->modifiedOn))
2277
			{
2278
				$where[] = 'e.`date`<='.$babDB->quote($this->modifiedOn);
2279
			}
2280
2281
			if (isset($this->appr_notified))
2282
			{
2283
				$where[] = 'e.appr_notified='.$babDB->quote($this->appr_notified);
2284
			}
2285
2286
			if (isset($this->idfai_set) && $this->idfai_set)
2287
			{
2288
				$where[] = 'e.idfai>'.$babDB->quote(0);
2289
			}
2290
2291
			if (isset($this->appr_idfai))
2292
			{
2293
				$where[] = 'e.idfai IN('.$babDB->quote($this->appr_idfai).')';
2294
			}
2295
2296
			if (isset($this->from))
2297
			{
2298
				$where[] = 'e.date_end >'.$babDB->quote($this->from);
2299
			}
2300
2301
			if (isset($this->to))
2302
			{
2303
				$where[] = 'e.date_begin <'.$babDB->quote($this->to);
2304
			}
2305
2306
			if (isset($this->startTo))
2307
			{
2308
				$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...
2309
			}
2310
2311
			if (isset($this->startFrom))
2312
			{
2313
				$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...
2314
			}
2315
2316
			if (isset($this->year))
2317
			{
2318
				$where[] = 'YEAR(e.date_begin)='.$babDB->quote($this->year);
2319
			}
2320
2321 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...
2322
			{
2323
				$req .= ', absences_personnel p';
2324
				$where[] = 'e.id_user=p.id_user';
2325
				$where[] = 'p.id_organization='.$babDB->quote($this->organization);
2326
			}
2327
2328
			if (isset($this->archived))
2329
			{
2330
				$where[] = 'e.archived='.$babDB->quote($this->archived);
2331
			}
2332
			
2333
			if (isset($this->id_right))
2334
			{
2335
			    $where[] = 'e.id IN(SELECT id_entry FROM absences_entries_elem WHERE id_right='.$babDB->quote($this->id_right).')';
2336
			}
2337
2338
			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...
2339
			{
2340
				$req .= ' WHERE '.implode(' AND ', $where);
2341
			}
2342
2343
			$req .= ' ORDER BY '.$this->orderby;
2344
			
2345
			$this->setMySqlResult($this->getDataBaseAdapter()->db_query($req));
2346
		}
2347
	}
2348
}
2349
2350
2351
/**
2352
 * Entry elements iterator
2353
 */
2354
class absences_EntryElemIterator extends absences_Iterator
2355
{
2356
	/**
2357
	 *
2358
	 * @var absences_Entry
2359
	 */
2360
	public $entry;
2361
2362
	/**
2363
	 * Filter by a list of id vaction types
2364
	 * @var int[]
2365
	 */
2366
	public $types;
2367
2368
2369
	public function getObject($data)
2370
	{
2371
		require_once dirname(__FILE__).'/type.class.php';
2372
		require_once dirname(__FILE__).'/right.class.php';
2373
		require_once dirname(__FILE__).'/entry_elem.class.php';
2374
2375
		$type_row = $this->getRowByPrefix($data, 'type');
2376
		$type = new absences_Type($type_row['id']);
2377
		$type->setRow($type_row);
2378
2379
		$right_row = $this->getRowByPrefix($data, 'right');
2380
		$right = new absences_Right($right_row['id']);
2381
		$right->setRow($right_row);
2382
		$right->setType($type);
2383
2384
		$elem_row = $this->getRowByPrefix($data, 'elem');
2385
		$elem = new absences_EntryElem();
2386
		$elem->setRow($elem_row);
2387
		$elem->setRight($right);
2388
		$elem->setEntry($this->entry);
2389
2390
		return $elem;
2391
	}
2392
2393
2394
2395
	public function executeQuery()
2396
	{
2397
		if(is_null($this->_oResult))
2398
		{
2399
			global $babDB;
2400
			$req = 'SELECT
2401
2402
				ee.id 					elem__id,
2403
				ee.id_entry 			elem__id_entry,
2404
				ee.id_right				elem__id_right,
2405
				ee.quantity				elem__quantity,
2406
			    ee.date_begin           elem__date_begin,
2407
			    ee.date_end             elem__date_end,
2408
2409
				r.id					right__id,
2410
				r.id_creditor			right__id_creditor,
2411
				r.kind					right__kind,
2412
			    r.createdOn			    right__createdOn,
2413
				r.date_entry			right__date_entry,
2414
				r.date_begin			right__date_begin,
2415
				r.date_end				right__date_end,
2416
				r.quantity				right__quantity,
2417
				r.quantity_unit			right__quantity_unit,
2418
				r.quantity_inc_month 	right__quantity_inc_month,
2419
				r.quantity_inc_max 		right__quantity_inc_max,
2420
				r.quantity_inc_last		right__quantity_inc_last,
2421
				r.id_type				right__id_type,
2422
				r.description			right__description,
2423
				r.active				right__active,
2424
				r.cbalance	 			right__cbalance,
2425
				r.date_begin_valid		right__date_begin_valid,
2426
				r.date_end_valid		right__date_end_valid,
2427
				r.date_end_fixed		right__date_end_fixed,
2428
				r.date_begin_fixed		right__date_begin_fixed,
2429
				r.no_distribution 		right__no_distribution,
2430
				r.use_in_cet			right__use_in_cet,
2431
				r.id_rgroup				right__id_rgroup,
2432
				r.earlier				right__earlier,
2433
				r.earlier_begin_valid 	right__earlier_begin_valid,
2434
				r.earlier_end_valid		right__earlier_end_valid,
2435
				r.later					right__later,
2436
				r.later_begin_valid		right__later_begin_valid,
2437
				r.later_end_valid		right__later_end_valid,
2438
				r.delay_before			right__delay_before,
2439
				r.id_report_type		right__id_report_type,
2440
				r.date_end_report		right__date_end_report,
2441
				r.description_report	right__description_report,
2442
				r.id_reported_from		right__id_reported_from,
2443
				r.quantity_alert_days	right__quantity_alert_days,
2444
				r.quantity_alert_types	right__quantity_alert_types,
2445
				r.quantity_alert_begin	right__quantity_alert_begin,
2446
				r.quantity_alert_end	right__quantity_alert_end,
2447
				r.dynconf_types			right__dynconf_types,
2448
				r.dynconf_begin			right__dynconf_begin,
2449
				r.dynconf_end			right__dynconf_end,
2450
				r.sync_status			right__sync_status,
2451
				r.sync_update			right__sync_update,
2452
				r.uuid					right__uuid,
2453
				r.archived				right__archived,
2454
				r.sortkey				right__sortkey,
2455
				r.require_approval		right__require_approval,
2456
2457
2458
				t.id					type__id,
2459
				t.name					type__name,
2460
				t.description			type__description,
2461
				t.color					type__color
2462
			FROM
2463
				absences_entries_elem ee,
2464
				absences_rights r,
2465
				absences_types t
2466
2467
			WHERE
2468
				ee.id_entry='.$babDB->quote($this->entry->id).'
2469
				AND ee.id_right = r.id
2470
				AND r.id_type = t.id';
2471
2472
			if (isset($this->types)) {
2473
			    $req .= ' AND t.id IN('.$babDB->quote($this->types).')';
2474
			}
2475
2476
		    $req .= ' ORDER BY ee.date_begin';
2477
2478
			$this->setMySqlResult($this->getDataBaseAdapter()->db_query($req));
2479
		}
2480
	}
2481
2482
2483
}
2484
2485
2486
2487
/**
2488
 * Entry periods iterator
2489
 */
2490
class absences_EntryPeriodIterator extends absences_Iterator
2491
{
2492
    /**
2493
     *
2494
     * @var absences_Entry
2495
     */
2496
    public $entry;
2497
2498
2499
2500
    public function getObject($data)
2501
    {
2502
        require_once dirname(__FILE__).'/entry_period.class.php';
2503
2504
2505
2506
        $elem_row = $this->getRowByPrefix($data, 'period');
2507
        $elem = new absences_EntryPeriod();
2508
        $elem->setRow($elem_row);
2509
        $elem->setEntry($this->entry);
2510
2511
        return $elem;
2512
    }
2513
2514
2515
2516
    public function executeQuery()
2517
    {
2518
        if(is_null($this->_oResult))
2519
        {
2520
            global $babDB;
2521
            $req = 'SELECT
2522
2523
				ep.id 					period__id,
2524
				ep.id_entry 			period__id_entry,
2525
			    ep.date_begin           period__date_begin,
2526
			    ep.date_end             period__date_end
2527
2528
			FROM
2529
				absences_entries_periods ep
2530
			WHERE
2531
				ep.id_entry='.$babDB->quote($this->entry->id).'';
2532
2533
2534
            $req .= ' ORDER BY ep.date_begin';
2535
2536
            $this->setMySqlResult($this->getDataBaseAdapter()->db_query($req));
2537
        }
2538
    }
2539
2540
2541
}
2542