Completed
Push — master ( 03fcbe...f90b04 )
by Paul
10:38
created

absences_ElementEntryIterator   A

Complexity

Total Complexity 3

Size/Duplication

Total Lines 141
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 5

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 0
loc 141
rs 10
c 0
b 0
f 0
ccs 0
cts 33
cp 0
wmc 3
lcom 0
cbo 5

3 Methods

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

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
2334 21
	        $where[] = 'e.id_user=p.id_user';
2335
	        $where[] = 'p.id_organization='.$babDB->quote($this->organization);
2336 21
	    }
2337 21
	    
2338 21
	    if (isset($this->archived))
2339 21
	    {
2340
	        $where[] = 'e.archived='.$babDB->quote($this->archived);
2341
	    }
2342 21
	    	
2343 21
	    if (isset($this->id_right))
2344 21
	    {
2345
	        $where[] = 'e.id IN(SELECT id_entry FROM absences_entries_elem WHERE id_right='.$babDB->quote($this->id_right).')';
2346 21
	    }
2347
	    
2348 21
	    return $where;
2349 21
	}
2350 21
2351
2352
2353
	public function executeQuery()
2354
	{
2355
		if(is_null($this->_oResult))
2356
		{
2357
			global $babDB;
2358
			$req = $this->getBaseQuery();
2359
2360
			$where = $this->getWhere();
2361
2362
			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...
2363
			{
2364
				$req .= ' WHERE '.implode(' AND ', $where);
2365
			}
2366
2367
			$req .= ' ORDER BY '.$this->orderby;
2368
			
2369
			$this->setMySqlResult($this->getDataBaseAdapter()->db_query($req));
2370
		}
2371
	}
2372
}
2373
2374
2375
/**
2376
 * Entry elements iterator joined on entries
2377
 */
2378
class absences_ElementEntryIterator extends absences_EntryIterator
2379
{
2380
    
2381
    public function getObject($data)
2382
    {
2383
        require_once dirname(__FILE__).'/type.class.php';
2384
        require_once dirname(__FILE__).'/right.class.php';
2385
        require_once dirname(__FILE__).'/entry_elem.class.php';
2386
        
2387
        $entry_row = $this->getRowByPrefix($data, 'entry');
2388
        $entry = new absences_Entry($entry_row['id']);
0 ignored issues
show
Unused Code introduced by
The call to absences_Entry::__construct() has too many arguments starting with $entry_row['id'].

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
2389
        $entry->setRow($entry_row);
2390
    
2391
        $type_row = $this->getRowByPrefix($data, 'type');
2392
        $type = new absences_Type($type_row['id']);
2393
        $type->setRow($type_row);
2394
    
2395
        $right_row = $this->getRowByPrefix($data, 'right');
2396
        $right = new absences_Right($right_row['id']);
2397
        $right->setRow($right_row);
2398
        $right->setType($type);
2399
    
2400
        $elem_row = $this->getRowByPrefix($data, 'elem');
2401
        $elem = new absences_EntryElem();
2402
        $elem->setRow($elem_row);
2403
        $elem->setRight($right);
2404
        $elem->setEntry($entry);
2405
    
2406
        return $elem;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $elem; (absences_EntryElem) is incompatible with the return type of the parent method absences_EntryIterator::getObject of type absences_Entry.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
2407
    }
2408
    
2409
    
2410
    /**
2411
     * @return string
2412
     */
2413
    protected function getBaseQuery()
2414
    {
2415
        return 'SELECT 
2416
                
2417
                    e.id                    entry__id,
2418
                    e.id_user               entry__id_user,
2419
                    e.createdby             entry__createdby,
2420
                    e.date_begin            entry__date_begin,
2421
                    e.date_end              entry__date_end,
2422
                    e.idfai                 entry__idfai,
2423
                    e.comment               entry__comment,
2424
                    e.createdOn             entry__createdOn,
2425
                    e.date                  entry__date,
2426
                    e.status                entry__status,
2427
                    e.comment2              entry__comment2,
2428
                    e.id_approver           entry__id_approver,
2429
                    e.folder                entry__folder,
2430
                    e.creation_type         entry__creation_type,
2431
                    e.appr_notified         entry__appr_notified,
2432
                    e.archived              entry__archived,
2433
                    e.todelete              entry__todelete,
2434
                    e.firstconfirm          entry__firstconfirm,
2435
                
2436
                    ee.id 					elem__id,
2437
    				ee.id_entry 			elem__id_entry,
2438
    				ee.id_right				elem__id_right,
2439
    				ee.quantity				elem__quantity,
2440
    			    ee.date_begin           elem__date_begin,
2441
    			    ee.date_end             elem__date_end,
2442
    
2443
    				r.id					right__id,
2444
    				r.id_creditor			right__id_creditor,
2445
    				r.kind					right__kind,
2446
    			    r.createdOn			    right__createdOn,
2447
    				r.date_entry			right__date_entry,
2448
    				r.date_begin			right__date_begin,
2449
    				r.date_end				right__date_end,
2450
    				r.quantity				right__quantity,
2451
    				r.quantity_unit			right__quantity_unit,
2452
    				r.quantity_inc_month 	right__quantity_inc_month,
2453
    				r.quantity_inc_max 		right__quantity_inc_max,
2454
    				r.quantity_inc_last		right__quantity_inc_last,
2455
    				r.id_type				right__id_type,
2456
    				r.description			right__description,
2457
    				r.active				right__active,
2458
    				r.cbalance	 			right__cbalance,
2459
    				r.date_begin_valid		right__date_begin_valid,
2460
    				r.date_end_valid		right__date_end_valid,
2461
    				r.date_end_fixed		right__date_end_fixed,
2462
    				r.date_begin_fixed		right__date_begin_fixed,
2463
    				r.no_distribution 		right__no_distribution,
2464
    				r.use_in_cet			right__use_in_cet,
2465
    				r.id_rgroup				right__id_rgroup,
2466
    				r.earlier				right__earlier,
2467
    				r.earlier_begin_valid 	right__earlier_begin_valid,
2468
    				r.earlier_end_valid		right__earlier_end_valid,
2469
    				r.later					right__later,
2470
    				r.later_begin_valid		right__later_begin_valid,
2471
    				r.later_end_valid		right__later_end_valid,
2472
    				r.delay_before			right__delay_before,
2473
    				r.id_report_type		right__id_report_type,
2474
    				r.date_end_report		right__date_end_report,
2475
    				r.description_report	right__description_report,
2476
    				r.id_reported_from		right__id_reported_from,
2477
    				r.quantity_alert_days	right__quantity_alert_days,
2478
    				r.quantity_alert_types	right__quantity_alert_types,
2479
    				r.quantity_alert_begin	right__quantity_alert_begin,
2480
    				r.quantity_alert_end	right__quantity_alert_end,
2481
    				r.dynconf_types			right__dynconf_types,
2482
    				r.dynconf_begin			right__dynconf_begin,
2483
    				r.dynconf_end			right__dynconf_end,
2484
    				r.sync_status			right__sync_status,
2485
    				r.sync_update			right__sync_update,
2486
    				r.uuid					right__uuid,
2487
    				r.archived				right__archived,
2488
    				r.sortkey				right__sortkey,
2489
    				r.require_approval		right__require_approval,
2490
    
2491
    
2492
    				t.id					type__id,
2493
    				t.name					type__name,
2494
    				t.description			type__description,
2495
    				t.color					type__color
2496
				FROM
2497
					absences_entries e,
2498
                    absences_entries_elem ee,
2499
                    absences_rights r,
2500
                    absences_types t 
2501
				';
2502
    
2503
    }
2504
    
2505
    /**
2506
     * @return array
2507
     */
2508
    protected function getWhere()
2509
    {
2510
        $where = parent::getWhere();
2511
        
2512
        $where[] = 'ee.id_entry=e.id';
2513
        $where[] = 'ee.id_right=r.id';
2514
        $where[] = 'r.id_type=t.id';
2515
        
2516
        return $where;
2517
    }
2518
}
2519 28
2520
2521 28
2522 28
/**
2523 28
 * Entry elements iterator
2524
 */
2525
class absences_EntryElemIterator extends absences_Iterator
2526
{
2527
	/**
2528
	 *
2529
	 * @var absences_Entry
2530
	 */
2531
	public $entry;
2532
2533
	/**
2534 28
	 * Filter by a list of id vaction types
2535
	 * @var int[]
2536
	 */
2537 28
	public $types;
2538
2539 28
2540 28
	public function getObject($data)
2541 28
	{
2542
		require_once dirname(__FILE__).'/type.class.php';
2543
		require_once dirname(__FILE__).'/right.class.php';
2544
		require_once dirname(__FILE__).'/entry_elem.class.php';
2545
2546
		$type_row = $this->getRowByPrefix($data, 'type');
2547
		$type = new absences_Type($type_row['id']);
2548
		$type->setRow($type_row);
2549
2550
		$right_row = $this->getRowByPrefix($data, 'right');
2551
		$right = new absences_Right($right_row['id']);
2552
		$right->setRow($right_row);
2553
		$right->setType($type);
2554
2555
		$elem_row = $this->getRowByPrefix($data, 'elem');
2556
		$elem = new absences_EntryElem();
2557
		$elem->setRow($elem_row);
2558
		$elem->setRight($right);
2559
		$elem->setEntry($this->entry);
2560
2561
		return $elem;
2562
	}
2563
2564
2565
2566
	public function executeQuery()
2567
	{
2568
		if(is_null($this->_oResult))
2569
		{
2570
			global $babDB;
2571
			$req = 'SELECT
2572
2573
				ee.id 					elem__id,
2574
				ee.id_entry 			elem__id_entry,
2575
				ee.id_right				elem__id_right,
2576
				ee.quantity				elem__quantity,
2577
			    ee.date_begin           elem__date_begin,
2578
			    ee.date_end             elem__date_end,
2579
2580
				r.id					right__id,
2581
				r.id_creditor			right__id_creditor,
2582
				r.kind					right__kind,
2583
			    r.createdOn			    right__createdOn,
2584
				r.date_entry			right__date_entry,
2585
				r.date_begin			right__date_begin,
2586
				r.date_end				right__date_end,
2587
				r.quantity				right__quantity,
2588
				r.quantity_unit			right__quantity_unit,
2589
				r.quantity_inc_month 	right__quantity_inc_month,
2590
				r.quantity_inc_max 		right__quantity_inc_max,
2591
				r.quantity_inc_last		right__quantity_inc_last,
2592
				r.id_type				right__id_type,
2593
				r.description			right__description,
2594
				r.active				right__active,
2595
				r.cbalance	 			right__cbalance,
2596
				r.date_begin_valid		right__date_begin_valid,
2597
				r.date_end_valid		right__date_end_valid,
2598
				r.date_end_fixed		right__date_end_fixed,
2599
				r.date_begin_fixed		right__date_begin_fixed,
2600
				r.no_distribution 		right__no_distribution,
2601
				r.use_in_cet			right__use_in_cet,
2602
				r.id_rgroup				right__id_rgroup,
2603
				r.earlier				right__earlier,
2604
				r.earlier_begin_valid 	right__earlier_begin_valid,
2605
				r.earlier_end_valid		right__earlier_end_valid,
2606
				r.later					right__later,
2607
				r.later_begin_valid		right__later_begin_valid,
2608
				r.later_end_valid		right__later_end_valid,
2609
				r.delay_before			right__delay_before,
2610
				r.id_report_type		right__id_report_type,
2611
				r.date_end_report		right__date_end_report,
2612
				r.description_report	right__description_report,
2613
				r.id_reported_from		right__id_reported_from,
2614
				r.quantity_alert_days	right__quantity_alert_days,
2615
				r.quantity_alert_types	right__quantity_alert_types,
2616
				r.quantity_alert_begin	right__quantity_alert_begin,
2617
				r.quantity_alert_end	right__quantity_alert_end,
2618
				r.dynconf_types			right__dynconf_types,
2619
				r.dynconf_begin			right__dynconf_begin,
2620
				r.dynconf_end			right__dynconf_end,
2621
				r.sync_status			right__sync_status,
2622
				r.sync_update			right__sync_update,
2623
				r.uuid					right__uuid,
2624
				r.archived				right__archived,
2625
				r.sortkey				right__sortkey,
2626
				r.require_approval		right__require_approval,
2627
2628
2629
				t.id					type__id,
2630
				t.name					type__name,
2631
				t.description			type__description,
2632
				t.color					type__color
2633
			FROM
2634
				absences_entries_elem ee,
2635
				absences_rights r,
2636
				absences_types t
2637
2638
			WHERE
2639
				ee.id_entry='.$babDB->quote($this->entry->id).'
2640
				AND ee.id_right = r.id
2641
				AND r.id_type = t.id';
2642
2643
			if (isset($this->types)) {
2644
			    $req .= ' AND t.id IN('.$babDB->quote($this->types).')';
2645
			}
2646
2647
		    $req .= ' ORDER BY ee.date_begin';
2648
2649
			$this->setMySqlResult($this->getDataBaseAdapter()->db_query($req));
2650
		}
2651
	}
2652
2653
2654
}
2655
2656
2657
2658
/**
2659
 * Entry periods iterator
2660
 */
2661
class absences_EntryPeriodIterator extends absences_Iterator
2662
{
2663
    /**
2664
     *
2665
     * @var absences_Entry
2666
     */
2667
    public $entry;
2668
2669
2670
2671
    public function getObject($data)
2672
    {
2673
        require_once dirname(__FILE__).'/entry_period.class.php';
2674
2675
2676
2677
        $elem_row = $this->getRowByPrefix($data, 'period');
2678
        $elem = new absences_EntryPeriod();
2679
        $elem->setRow($elem_row);
2680
        $elem->setEntry($this->entry);
2681
2682
        return $elem;
2683
    }
2684
2685
2686
2687
    public function executeQuery()
2688
    {
2689
        if(is_null($this->_oResult))
2690
        {
2691
            global $babDB;
2692
            $req = 'SELECT
2693
2694
				ep.id 					period__id,
2695
				ep.id_entry 			period__id_entry,
2696
			    ep.date_begin           period__date_begin,
2697
			    ep.date_end             period__date_end
2698
2699
			FROM
2700
				absences_entries_periods ep
2701
			WHERE
2702
				ep.id_entry='.$babDB->quote($this->entry->id).'';
2703
2704
2705
            $req .= ' ORDER BY ep.date_begin';
2706
2707
            $this->setMySqlResult($this->getDataBaseAdapter()->db_query($req));
2708
        }
2709
    }
2710
2711
2712
}
2713