Completed
Push — master ( 6fc3a1...47798a )
by Paul
03:19
created

absences_Entry   F

Complexity

Total Complexity 282

Size/Duplication

Total Lines 2393
Duplicated Lines 12.95 %

Coupling/Cohesion

Components 1
Dependencies 19

Test Coverage

Coverage 37.69%

Importance

Changes 8
Bugs 4 Features 2
Metric Value
dl 310
loc 2393
rs 0.6314
c 8
b 4
f 2
ccs 366
cts 971
cp 0.3769
wmc 282
lcom 1
cbo 19

84 Methods

Rating   Name   Duplication   Size   Complexity  
A getElements() 0 4 1
A addPeriod() 0 4 1
A getPlannedPeriods() 0 4 1
A sortElements() 0 4 1
A addElement() 0 4 1
A getTotalDays() 17 17 4
A getTotalHours() 17 17 4
A requireApproval() 0 17 4
A getById() 0 7 1
A getRow() 0 16 3
A getRequestType() 0 15 3
A getApprobationId() 0 9 2
A getElementsIterator() 0 7 1
A getPlannedPeriodsIterator() 0 7 1
A getFolderEntriesIterator() 12 12 2
A addElementsMovements() 0 8 2
A loadElements() 0 15 3
A loadPlannedPeriods() 0 21 4
A getPlannedDurationHours() 14 14 3
A sortElem() 0 18 3
B checkElementsDates() 0 14 6
B getNextValidDate() 0 23 5
A getFutureDate() 0 8 2
B getWorkingPeriodsFromDate() 0 23 5
B getFutureDate_Seconds() 0 30 4
A getFutureDate_Hours() 0 6 1
C getWorkingTimeByDay() 6 51 10
B getSecondsToAdd() 0 30 5
B getFutureDate_Days_Old() 0 45 6
A createPlannedPeriods() 0 17 2
B getPeriodQuantity() 0 27 6
A getDayDurationInHours() 0 18 3
B getPeriodType() 0 19 7
B saveElements() 0 26 3
A savePlannedPeriods() 0 21 3
A applyDynamicRight() 0 13 2
A removeElement() 0 13 3
A setWorkingPeriodIndex() 0 5 1
B getWorkingPeriodIndex() 0 25 4
B loadDurations() 0 27 4
A getWorkingPeriods() 0 8 2
A getDayWorkingPeriods() 0 11 3
A getDayPlannedPeriods() 0 11 3
A getDurationDays() 0 9 2
A getDurationHours() 0 9 2
A daysToHours() 12 12 2
A hoursToDays() 10 10 2
B loadedElementsTotal() 0 32 5
A addQUantityInCache() 0 15 3
B getPlannedDaysBetween() 30 30 6
B getPlannedHoursBetween() 30 30 6
A containsHours() 0 12 3
C getElement() 0 48 7
A getPlannedDurationDays() 18 18 3
F loadDefaultValues() 0 40 10
B save() 37 82 4
C setElementsDates() 0 45 7
A getElementEndGap() 0 15 3
D getFutureDate_Days() 0 109 14
A checkAvailable() 0 10 3
A checkElementsValidity() 0 16 3
A checkOtherEntriesValidity() 6 21 3
C checkValidity() 14 105 8
A modifiedOn() 0 4 1
A isFixed() 0 5 1
A isPrevisonal() 0 4 1
A isFolderPrevisonal() 0 13 2
A getFolderFirst() 17 17 3
A onReject() 0 4 1
A onConfirm() 0 4 1
B updateCalendar() 0 29 2
A getTitle() 0 8 3
A getNotifyFields() 8 8 1
A getYear() 11 11 2
A getArchiveYear() 20 20 3
A archive() 11 11 1
A setNotified() 16 16 2
A getManagerEditUrl() 0 6 1
A getManagerDeleteUrl() 0 9 1
A getManagerFrame() 0 15 2
C getCardFrame() 4 74 9
A getEditUrl() 0 17 3
B delete() 0 57 8
A notifyOwner() 0 19 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like absences_Entry often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use absences_Entry, and based on these observations, apply Extract Interface, too.

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

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

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

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

    return false;
}

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

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

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

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

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

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

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

$answer = 42;

$correct = false;

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

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

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

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

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

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

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

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

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

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

There are different options of fixing this problem.

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

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

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

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

There are different options of fixing this problem.

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

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

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

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

There are different options of fixing this problem.

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

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

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

Loading history...
862
	        
863
	        
864 20
	        if ($start >= $period->ts_end) {
865 15
	            continue;
866
	        }
867
	        
868
	        
869
	        
870 20
	        $d = date('Y-m-d', $period->ts_begin);
871
	        
872 20
	        if (!isset($days[$d])) {
873
	            
874 20
	            $days[$d] = array(
875 20
	                'duration' => 0,
876 20
	                'type' => null,
877
	                'dayend' => null
878 20
	            );
879 20
	        }
880
	        
881
	            
882 20
	        $days[$d]['duration'] += $period->getDuration();
883
	        
884 20
	        if ($period->ts_end > $days[$d]['dayend']) {
885 20
	            $days[$d]['dayend'] = $period->ts_end;
886 20
	        }
887
	        
888
	        
889 20
	        $type = $this->getPeriodType($period);
890
	        
891 20 View Code Duplication
	        if ('AM' === $days[$d]['type'] && 'PM' === $type) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

Loading history...
896
	            $days[$d]['type'] = 'AMPM';
897
	        }
898
	        
899 20
	        if (!isset($days[$d]['type'])) {
900 20
	            $days[$d]['type'] = $type;
901 20
	        }
902 20
	    }
903
	    
904
	    
905
	    
906 20
	    return $days;
907
	}
908
	
909
	
910
	
911
912
	
913
	
914
	
915
	
916
	/**
917
	 * Add quantity to startdate only on working periods
918
	 *
919
	 * @param BAB_DateTime $startdate
920
	 * @param float $quantity  days
921
	 *
922
	 * @return BAB_DateTime
923
	 */
924 20
	protected function getFutureDate_Days(BAB_DateTime $startdate, $quantity)
925
	{
926
	    
927 20
	    $return = clone $startdate;
928
	    
929 20
	    $morning = clone $startdate;
930 20
	    $morning->setIsoTime('00:00:00');
931
	    
932 20
	    $days = $this->getWorkingTimeByDay($morning);
933
	    
934
	    
935 20
	    $seconds_to_add = 0;
936
937
	    
938
	    // traiter les jours entiers
939
	    
940 20
	    $fulldays = $lastfulldays = (float) floor($quantity);
941 20
	    $lastquantity = $quantity;
942 20
	    $lastdays = $days;
943 20
	    $day = key($days);
0 ignored issues
show
Unused Code introduced by
$day is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
944
	    
945 20
	    $pos = 0;
946 20
	    if (0 < $fulldays) {
947
948 15
	        foreach($days as $keyday => $arr) {
949
950 15
	            $dayquantity = 'AMPM' === $arr['type'] ? 1.0 : 0.5;
951
952 15
	            $lastfulldays -= $dayquantity;
953
954 15
	            if ($lastfulldays <= 0) {
955 15
	                $lastquantity = $quantity - $fulldays - $lastfulldays;
956
957 15
	                if ($pos > 0) {
958 9
	                    $lastdays = array_slice($days, $pos, null, true);
959
	                    
960 9
	                } else {
961 12
	                    $lastdays = $days;
962
	                }
963
964 15
	                $day = $keyday;
0 ignored issues
show
Unused Code introduced by
$day is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
965
	                
966 15
	                break;
967
	            }
968
969 9
	            $pos++;
970 15
	        }
971
	        
972
	        
973
	        
974
975
	        
976 15
	        if ($fulldays > 0 && $lastquantity <= 0) {
977
	            
978
	            
979
	            
980
	            // il ne reste pas de temps a appliquer sur la date
981
	            // on se place donc a la fin d'une periode travaillee
982
983 7
	            reset($days);
984 7
	            for($i=0; $i<$fulldays-1; $i++) {
985 6
	                next($days);
986 6
	            }
987
	            
988 7
	            $newstart = current($days);
989 7
	            $return = BAB_DateTime::fromTimeStamp($newstart['dayend']);
990
	            
991
	            //echo $fulldays.' '.$startdate->getIsoDateTime().' -> '.$return->getIsoDateTime()."\n";
992
	            
993 7
	        } else {
994
	            
995
	            // il reste du temps a ajouter sur la date
996
	            // on ajoute juste le nombre de jours complets
997
	            
998 8
	            $return->add($fulldays, BAB_DATETIME_DAY);
999
	        }
1000
	        
1001
	        
1002
	        
1003
	        // si la date de debut est supperieur a la date d'ouverture de la premiere periode travaillee
1004
	        // on considere que la date de debut est sur la prochaine periode travaillee, par exemple commence l'apres-midi
1005 15
	        $start = reset($days);
1006 15
	        $fromstart = $this->getWorkingTimeByDay($startdate);
1007 15
	        $firstday = reset($fromstart);
1008 15
	        $missing_duration = $start['duration'] - $firstday['duration'];
1009 15
	        if ($missing_duration > 0) {
1010
	            // var_dump($startdate->getIsoDateTime().' '.($missing_duration/3600).'H');
1011 3
	            $seconds_to_add = $missing_duration;
1012 3
	        }
1013 15
	    }
1014
	    
1015
	    
1016
	    
1017 20
	    if ($seconds_to_add === 0 && ($lastquantity <= 0 || !isset($lastdays))) {
1018 6
	        return $return;
1019
	    }
1020
	    
1021 15
	    $seconds_to_add += $this->getSecondsToAdd($lastdays, $lastquantity);
1022
	    
1023
	    
1024 15
	    if (0 === $seconds_to_add) {
1025
	        return $return;
1026
	    }
1027
1028
	    
1029 15
	    $return = $this->getFutureDate_Seconds($return, $seconds_to_add);
1030
	    
1031 15
	    return $return;
1032
	}
1033
	
1034
	
1035
	/**
1036
	 * Trouver le nombre de secondes a ajouter a une date pour une quantite 
1037
	 * @param array $lastdays      Jours restants sur la periode
1038
	 * @param float $lastquantity  Nombre de jours a ajouter, uniquement les jours incomplets de la fin de periode, les jours complets ont ete traites avant
1039
	 * 
1040
	 */
1041 15
	protected function getSecondsToAdd(Array $lastdays, $lastquantity)
1042
	{
1043 15
	    $seconds_to_add = 0;
1044
	    
1045
	    //var_dump('lastquantity: '.$lastquantity);
1046
	    
1047
	    // trouver les heures a partir de la quantite restante
1048
	    
1049 15
	    foreach($lastdays as $keyday => $arr) {
1050
	        
1051 15
	        $q = 'AMPM' === $arr['type'] ? 1.0 : 0.5;
1052
	    
1053 15
	        if ($lastquantity > $q) {
1054
	            $lastquantity -= $q;
1055
	            continue;
1056
	        }
1057
	    
1058
	        //var_dump(($arr['duration']/3600).' '.$lastquantity.' '.$arr['type'].' '.$q);
1059
	        
1060 15
	        if ($lastquantity == $q) {
1061 1
	            $seconds_to_add = $arr['duration'];
1062 1
	            break;
1063
	        }
1064
	    
1065 14
	        $seconds_to_add = $arr['duration'] * $lastquantity;
1066 14
	        break;
1067 15
	    }
1068
	    
1069 15
	    return $seconds_to_add;
1070
	}
1071
	
1072
	
1073
1074
	/**
1075
	 * Add quantity to startdate only on working periods
1076
	 * 
1077
	 * @deprecated probleme avec les periodes a cheval sur 12:00 conbinne aux quantites inferieurs a 1 jour
1078
	 *
1079
	 * @param BAB_DateTime $startdate
1080
	 * @param float $quantity  days
1081
	 *
1082
	 * @return BAB_DateTime
1083
	 */
1084
	protected function getFutureDate_Days_Old(BAB_DateTime $startdate, $quantity)
1085
	{
1086
	    
1087
	    $start = $startdate->getTimeStamp();
1088
1089
	    $periods_quantity = 0; // days
1090
1091
        // get the number of worked hours from startdate to the end of the day
1092
 
1093
	    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...
1094
1095
	        if ($start >= $period->ts_end) {
1096
	            // continue to the next worked period
1097
	            //var_dump('working period ignored, start='.bab_shortDate($start, true).' >= '.bab_shortDate($period->ts_end, true));
1098
	            continue;
1099
	        }
1100
1101
	        $periods_quantity += $this->getPeriodQuantity($startdate, $period);
1102
	        
1103
1104
	        if ($periods_quantity >= $quantity) {
1105
1106
	            $overhead = $periods_quantity - $quantity;
1107
1108
	            $futureDate = BAB_DateTime::fromTimeStamp($period->ts_end);
1109
1110
	            // var_dump($futureDate->getIsoDateTime().' overhead='.$overhead.' ('.$periods_quantity.' - '.$quantity.')');
1111
1112
	            if ($overhead) {
1113
	                $futureDate->less(
1114
	                    3600 * $this->getDayDurationInHours($futureDate->getIsoDate(), $overhead),
1115
	                    BAB_DATETIME_SECOND
1116
	                );
1117
	            }
1118
1119
	            if ($futureDate->getIsoDateTime() > $this->date_end) {
1120
	                $futureDate = BAB_DateTime::fromIsoDateTime($this->date_end);
1121
	            }
1122
1123
	            return $futureDate;
1124
	        }
1125
	    }
1126
1127
	    return BAB_DateTime::fromIsoDateTime($this->date_end);
1128
	}
1129
1130
1131
	/**
1132
	 * Create the planned periods in the entry boundaries
1133
	 * from the calendar working periods
1134
	 */
1135 23
	public function createPlannedPeriods()
1136
	{
1137 23
	    require_once dirname(__FILE__).'/entry_period.class.php';
1138 23
	    $this->plannedPeriods = array();
1139
1140 23
	    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...
1141
1142
	        /*@var $workingPeriod bab_CalendarPeriod */
1143
1144 23
	        $plannedPeriod = new absences_EntryPeriod();
1145 23
	        $plannedPeriod->setEntry($this);
1146 23
	        $plannedPeriod->date_begin = date('Y-m-d H:i:s', $workingPeriod->ts_begin);
1147 23
	        $plannedPeriod->date_end = date('Y-m-d H:i:s', $workingPeriod->ts_end);
1148
1149 23
	        $this->plannedPeriods[] = $plannedPeriod;
1150 23
	    }
1151 23
	}
1152
1153
1154
	/**
1155
	 * Get period quantity in days from a datetime
1156
	 *
1157
	 * @param BAB_DateTime         $startdate
1158
	 * @param bab_CalendarPeriod   $p
1159
	 *
1160
	 * @return float
1161
	 */
1162
	protected function getPeriodQuantity(BAB_DateTime $startdate, bab_CalendarPeriod $p)
1163
	{
1164
	    $type = $this->getPeriodType($p);
1165
1166
	    if ('AMPM' === $type) {
1167
	        $period_days = 1.0;
1168
	    }
1169
1170
	    if ('AM' === $type || 'PM' === $type) {
1171
	        $period_days = 0.5;
1172
	    }
1173
1174
	    $moment = $startdate->getTimeStamp();
1175
	    
1176
	    
1177
	    if ($moment > $p->ts_begin && $moment < $p->ts_end) {
1178
	        $period_seconds = $p->getDuration();
1179
	        $new_period = clone $p;
1180
	        $new_period->setBeginDate($startdate);
1181
1182
	        $new_seconds = $new_period->getDuration();
1183
1184
	        $period_days = ($new_seconds * $period_days) / $period_seconds;
0 ignored issues
show
Bug introduced by
The variable $period_days does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

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

There are different options of fixing this problem.

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

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

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

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

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

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

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

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

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

Loading history...
1547
	{
1548
		if (0 == $this->getTotalHours())
1549
		{
1550
			return 0;
1551
		}
1552
1553
		$ratio = $this->getPlannedDurationDays() / $this->getPlannedDurationHours();
1554
		return round(($ratio * $hours), 2);
1555
	}
1556
1557
1558
1559
	/**
1560
	 * Compute totals for loaded elements
1561
	 */
1562
	private function loadedElementsTotal()
1563
	{
1564
		if (empty($this->elements))
1565
		{
1566
			$this->loadElements();
1567
		}
1568
1569
		$this->total_days = 0.0;
1570
		$this->total_hours = 0.0;
1571
		$this->total_type_days = array();
1572
		$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...
1573
1574
		foreach($this->elements as $elem)
1575
		{
1576
			/*@var $elem absences_EntryElem */
1577
			$right = $elem->getRight();
1578
1579
			$quantity = (float) $elem->quantity;
1580
1581
			switch($right->quantity_unit)
1582
			{
1583
				case 'D':
1584
					$hours = $this->daysToHours($quantity);
1585
					$this->addQUantityInCache($right, $quantity, $hours);
1586
					break;
1587
				case 'H':
1588
				    $days = $this->hoursToDays($quantity);
1589
				    $this->addQUantityInCache($right, $days, $quantity);
1590
					break;
1591
			}
1592
		}
1593
	}
1594
1595
1596
	/**
1597
	 * Populate the cache variables for one element
1598
	 *
1599
	 * @param absences_Right $right
1600
	 * @param float $days
1601
	 * @param float $hours
1602
	 */
1603
	private function addQUantityInCache(absences_Right $right, $days, $hours)
1604
	{
1605
	    if (!isset($this->total_type_days[$right->id_type])) {
1606
	        $this->total_type_days[$right->id_type] = 0.0;
1607
	    }
1608
1609
	    if (!isset($this->total_type_hours[$right->id_type])) {
1610
	        $this->total_type_hours[$right->id_type] = 0.0;
1611
	    }
1612
1613
	    $this->total_days += $days;
1614
	    $this->total_hours += $hours;
1615
	    $this->total_type_days[$right->id_type] += $days;
1616
	    $this->total_type_hours[$right->id_type] += $hours;
1617
	}
1618
1619
1620
1621
1622
1623
	/**
1624
	 * Get sum of elements quantity, converted in days
1625
	 *
1626
	 * @param int $id_type Optional filter by type
1627
	 *
1628
	 * @return float
1629
	 */
1630 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...
1631
	{
1632
		if (!isset($this->total_days)) {
1633
			$this->loadedElementsTotal();
1634
		}
1635
1636
		if (isset($id_type)) {
1637
1638
		    if (!isset($this->total_type_days[$id_type])) {
1639
		        return 0.0;
1640
		    }
1641
1642
		    return $this->total_type_days[$id_type];
1643
		}
1644
1645
		return $this->total_days;
1646
	}
1647
1648
	/**
1649
	 * Get sum of elements quantity, converted in hours
1650
	 *
1651
	 * @param int $id_type Optional filter by type
1652
	 *
1653
	 * @return float
1654
	 */
1655 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...
1656
	{
1657
		if (!isset($this->total_hours)) {
1658
			$this->loadedElementsTotal();
1659
		}
1660
1661
		if (isset($id_type)) {
1662
1663
		    if (!isset($this->total_type_hours[$id_type])) {
1664
		        return 0.0;
1665
		    }
1666
1667
		    return $this->total_type_hours[$id_type];
1668
		}
1669
1670
		return $this->total_hours;
1671
	}
1672
1673
1674
1675
1676
	/**
1677
	 * Number of days on entry between two dates
1678
	 * si les dates de l'element sont a cheval sur la periode demandee
1679
	 * on utlise les heures travailles qui etait en vigeur au moment de la creation
1680
	 * de la demande
1681
	 *
1682
	 * @param string   $begin	Datetime
1683
	 * @param string   $end		Datetime
1684
	 * @param int      $id_type Optional filter by type
1685
	 *
1686
	 * @return float (days)
1687
	 */
1688 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...
1689
	{
1690 1
	    if (empty($this->elements))
1691 1
	    {
1692
	        $this->loadElements();
1693
	    }
1694
1695 1
	    $total = 0.0;
1696 1
	    foreach($this->elements as $elem)
1697
	    {
1698
	        /*@var $elem absences_EntryElem */
1699 1
	        $right = $elem->getRight();
1700
1701 1
	        if (isset($id_type) && (int) $id_type !== (int) $right->id_type) {
1702
	            continue;
1703
	        }
1704
1705 1
	        $quantity = $elem->getPlannedQuantityBetween($begin, $end);
1706
1707 1
	        if ('H' === $right->quantity_unit) {
1708
	            $days = $this->hoursToDays($quantity);
1709
	        } else {
1710 1
	            $days = $quantity;
1711
	        }
1712
1713 1
	        $total += $days;
1714 1
	    }
1715
1716 1
	    return $total;
1717
	}
1718
1719
1720
1721
1722
	/**
1723
	 * Number of hours on entry between two dates
1724
	 * si les dates de l'element sont a cheval sur la periode demandee
1725
	 * on utlise les heures travailles qui etait en vigeur au moment de la creation
1726
	 * de la demande
1727
	 *
1728
	 * @param string   $begin	Datetime
1729
	 * @param string   $end		Datetime
1730
	 * @param int      $id_type Optional filter by type
1731
	 *
1732
	 * @return float (hours)
1733
	 */
1734 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...
1735
	{
1736
	    if (empty($this->elements))
1737
	    {
1738
	        $this->loadElements();
1739
	    }
1740
1741
	    $total = 0.0;
1742
	    foreach($this->elements as $elem)
1743
	    {
1744
	        /*@var $elem absences_EntryElem */
1745
	        $right = $elem->getRight();
1746
1747
	        if (isset($id_type) && (int) $id_type !== (int) $right->id_type) {
1748
	            continue;
1749
	        }
1750
1751
	        $quantity = $elem->getPlannedQuantityBetween($begin, $end);
1752
1753
	        if ('D' === $right->quantity_unit) {
1754
	            $hours = $this->daysToHours($quantity);
1755
	        } else {
1756
	            $hours = $quantity;
1757
	        }
1758
1759
	        $total += $hours;
1760
	    }
1761
1762
	    return $total;
1763
	}
1764
1765
1766
1767
1768
1769
1770
1771
1772
	/**
1773
	 * Test if the loaded elements in entry contains rights in hours
1774
	 * @return bool
1775
	 */
1776
	public function containsHours()
1777
	{
1778
		foreach($this->elements as $elem)
1779
		{
1780
			if ('H' === $elem->getRight()->quantity_unit)
1781
			{
1782
				return true;
1783
			}
1784
		}
1785
1786
		return false;
1787
	}
1788
1789
1790
	/**
1791
	 * Test if loaded elements in entry require approval
1792
	 *
1793
	 * @return bool
1794
	 */
1795
	public function requireApproval()
1796
	{
1797
		if (empty($this->elements))
1798
		{
1799
			$this->loadElements();
1800
		}
1801
1802
		foreach($this->elements as $elem)
1803
		{
1804
			if (1 === (int) $elem->getRight()->require_approval)
1805
			{
1806
				return true;
1807
			}
1808
		}
1809
1810
		return false;
1811
	}
1812
	
1813
	
1814
	public function checkAvailable()
1815
	{
1816
	    $res = $this->getElementsIterator();
1817
	    foreach ($res as $element) {
1818
	        /*@var $element absences_EntryElem */
1819
	        if (!$element->isQuantityAvailable()) {
1820
	            throw new Exception(sprintf(absences_translate('Failed to submit this request, the quantity for right %s s not available'), $element->getRight()->description));
1821
	        }
1822
	    }
1823
	}
1824
1825
	
1826
	/**
1827
	 * Check elements validity
1828
	 * @throws UnexpectedValueException
1829
	 * 
1830
	 * @return int number of elements with quantity > 0
1831
	 */
1832
	protected function checkElementsValidity()
1833
	{
1834
	    $count = 0;
1835
	    foreach($this->elements as $elem)
1836
	    {
1837
	        $quantity = (int) round(100 * $elem->quantity);
1838
	        if ($quantity > 0)
1839
	        {
1840
	            $count++;
1841
	        }
1842
	    
1843
	        $elem->checkValidity();
1844
	    }
1845
	    
1846
	    return $count;
1847
	}
1848
	
1849
	
1850
	/**
1851
	 * throw an exception if there is another entry in the same period
1852
	 * @throws absences_EntryException
1853
	 */
1854
	protected function checkOtherEntriesValidity()
1855
	{
1856
	    $otherEntries = new absences_EntryIterator();
1857
	    $otherEntries->from = $this->date_begin;
1858
	    $otherEntries->to = $this->date_end;
1859
	    $otherEntries->users = array($this->id_user);
1860
	    
1861
	    
1862
	    foreach ($otherEntries as $otherEntry) {
1863
	    
1864
	        /* @var $otherEntry absences_Entry */
1865
	    
1866 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...
1867
	            $e = new absences_EntryException(absences_translate('There is allready an absence request in the period'));
1868
	            $e->entry = $this;
1869
	            $e->blocking = true;
1870
	            throw $e;
1871
	        }
1872
	    }
1873
	    
1874
	}
1875
1876
1877
	/**
1878
	 * Check request validity
1879
	 *
1880
	 * @throws absences_EntryException
1881
	 * @throws UnexpectedValueException
1882
	 * 
1883
	 * @return bool
1884
	 */
1885
	public function checkValidity()
1886
	{
1887
		// verify mandatory data
1888
1889
		if (!isset($this->id_user) || $this->id_user <= 0)
1890
		{
1891
			throw new UnexpectedValueException('Unexpected id_user');
1892
		}
1893
1894
1895
		$count = $this->checkElementsValidity();
1896
1897
1898 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...
1899
		{
1900
			$e = new absences_EntryException(absences_translate('At least one vacation right must be used'));
1901
			$e->entry = $this;
1902
			$e->blocking = true;
1903
			throw $e;
1904
		}
1905
1906
1907
1908
		// test user validity
1909
1910
		require_once $GLOBALS['babInstallPath'].'utilit/userinfosincl.php';
1911
1912
		$creationdate = bab_userInfos::getCreationDate($this->id_user);
1913
1914
		if ($creationdate >= $this->date_end) {
1915
			$e = new absences_EntryException(sprintf(absences_translate('The vacation request end before user creation date : %s'), bab_shortDate(bab_mktime($creationdate))));
1916
			$e->entry = $this;
1917
			$e->blocking = true;
1918
			throw $e;
1919
		}
1920
1921
		// T8359 : si l'utilisateur est reactive la demande ne sera pas presente, il faudra reenregistrer le droit
1922
		// avant la correction de ce ticket, on creeais les demandes pour eviter ce probleme
1923
1924
		if (!bab_userInfos::isValid($this->id_user)) {
1925
1926
		    /*
1927
		    $e = new absences_EntryException(absences_translate('This user account is disabled'));
1928
		    $e->entry = $this;
1929
		    $e->blocking = true;
1930
		    throw $e;
1931
		    */
1932
1933
		    return false;
1934
		}
1935
1936
1937
		// test quantity / period duration in days
1938
1939
		$total = (int) round(100 * $this->getTotalDays());
1940
		$duration = (int) round(100 * $this->getDurationDays());
1941
1942
1943 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...
1944
		{
1945
			$e = new absences_EntryException(absences_translate('The selected period is not available'));
1946
			$e->entry = $this;
1947
			$e->blocking = true;
1948
			throw $e;
1949
		}
1950
1951
1952
1953
		if ($total !== $duration)
1954
		{
1955
			$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')));
1956
			$e->entry = $this;
1957
			$e->blocking = !((bool) absences_getVacationOption('allow_mismatch'));
1958
			throw $e;
1959
		}
1960
1961
		// test quantity / period duration in hours only if there is one right in hours
1962
		/*
1963
		 *
1964
		 * 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
1965
		 * 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)
1966
		 *
1967
		if ($this->containsHours())
1968
		{
1969
			$total = (int) round(100 * $this->getTotalHours());
1970
			$duration = (int) round(100 * $this->getDurationHours());
1971
1972
			if ($total !== $duration)
1973
			{
1974
				$e = new absences_EntryException(absences_translate('The total quantity does not match the period duration in hours'));
1975
				$e->blocking = !((bool) absences_getVacationOption('allow_mismatch'));
1976
				throw $e;
1977
			}
1978
		}
1979
1980
		*/
1981
1982
1983
1984
		// verifier si la periode demandee n'est pas deja occuppe par une autre demande
1985
        $this->checkOtherEntriesValidity();
1986
		
1987
1988
		return true;
1989
	}
1990
1991
1992
	/**
1993
	 * (non-PHPdoc)
1994
	 * @see absences_Request::modifiedOn()
1995
	 *
1996
	 * @return string
1997
	 */
1998 1
	public function modifiedOn()
1999
	{
2000 1
		return $this->date;
2001
	}
2002
2003
	/**
2004
	 * Test if the entry is a fixed vacation
2005
	 * @return bool
2006
	 */
2007
	public function isFixed()
2008
	{
2009
		return (1 === (int) $this->creation_type);
2010
2011
	}
2012
2013
2014
2015
2016
2017
	/**
2018
	 * Test if entry is previsonal
2019
	 * @return bool
2020
	 */
2021
	public function isPrevisonal()
2022
	{
2023
		return ($this->status === 'P');
2024
	}
2025
2026
	/**
2027
	 * Test if at least one entry in folder is previsonal
2028
	 * @return boolean
2029
	 */
2030
	public function isFolderPrevisonal()
2031
	{
2032
		if (!$this->folder)
2033
		{
2034
			return false;
2035
		}
2036
2037
		$I = new absences_EntryIterator;
2038
		$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...
2039
		$I->status = 'P';
2040
2041
		return ($I->count() > 0);
2042
	}
2043
2044
	/**
2045
	 * Get the first entry in folder
2046
	 * @return absences_Entry
2047
	 */
2048 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...
2049
	{
2050
		if (!$this->folder)
2051
		{
2052
			return null;
2053
		}
2054
2055
		$I = new absences_EntryIterator;
2056
		$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...
2057
2058
		foreach($I as $first)
2059
		{
2060
			return $first;
2061
		}
2062
2063
		return null;
2064
	}
2065
2066
2067
	/**
2068
	 * Process specific code when the request is rejected
2069
	 *
2070
	 */
2071
	protected function onReject()
2072
	{
2073
		$this->updateCalendar();
2074
	}
2075
2076
2077
	/**
2078
	 * Process specific code when the request is confirmed
2079
	 *
2080
	 */
2081
	public function onConfirm()
2082
	{
2083
		$this->updateCalendar();
2084
	}
2085
2086
2087
2088
	/**
2089
	 * Call the user calendar backend to update the event
2090
	 * call the event to notify about calendar event modification
2091
	 */
2092
	public function updateCalendar()
2093
	{
2094
		require_once $GLOBALS['babInstallPath'].'utilit/dateTime.php';
2095
2096
		// try to update event copy in other backend (caldav)
2097
2098
		$begin = BAB_DateTime::fromIsoDateTime($this->date_begin);
2099
		$end = BAB_DateTime::fromIsoDateTime($this->date_end);
2100
		$period = absences_getPeriod($this->id, $this->id_user,  $begin, $end);
2101
		if ($period) {
2102
2103
			// probably set a new description if the event has been approved or rejected
2104
			absences_setPeriodProperties($period, $this);
2105
2106
			// save copy of event to calendar backend (if caldav)
2107
			$period->save();
2108
		}
2109
2110
2111
		// Update calendar data overlapped with event
2112
2113
		$date_begin = bab_mktime($this->date_begin);
2114
		$date_end	= bab_mktime($this->date_end);
2115
2116
		include_once $GLOBALS['babInstallPath']."utilit/eventperiod.php";
2117
		$event = new bab_eventPeriodModified($date_begin, $date_end, $this->id_user);
2118
		$event->types = BAB_PERIOD_VACATION;
0 ignored issues
show
Bug introduced by
The property types cannot be accessed from this context as it is declared private in class bab_eventPeriodModified.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
2119
		bab_fireEvent($event);
2120
	}
2121
2122
2123
	public function getTitle()
2124
	{
2125
	    if (isset($this->todelete) && $this->todelete) {
2126
	        return absences_translate('vacation request to delete');
2127
	    }
2128
	    
2129
		return absences_translate('vacation request');
2130
	}
2131
2132
2133 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...
2134
	{
2135
		return array(
2136
			absences_translate('From') 		=> bab_shortDate(bab_mktime($this->date_begin)),
2137
			absences_translate('Until')		=> bab_shortDate(bab_mktime($this->date_end)),
2138
			absences_translate('Quantity') 	=> absences_quantity($this->getTotalDays(), 'D')
2139
		);
2140
	}
2141
2142
2143 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...
2144
	{
2145
		$year = (int) substr($this->date_begin, 0, 4);
2146
2147
		if (0 === $year)
2148
		{
2149
			return null;
2150
		}
2151
2152
		return $year;
2153
	}
2154
2155
2156 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...
2157
	{
2158
		require_once $GLOBALS['babInstallPath'].'utilit/dateTime.php';
2159
		$year = (int) substr($this->date_begin, 0, 4);
2160
2161
		if (0 === $year)
2162
		{
2163
			return null;
2164
		}
2165
2166
		$day = absences_getVacationOption('archivage_day');
2167
		$month = absences_getVacationOption('archivage_month');
2168
2169
		$currentYear = new BAB_DateTime($year, $month, $day);
2170
		if($this->date_begin < $currentYear->getIsoDate()){
2171
			$year = $year-1;
2172
		}
2173
2174
		return $year;
2175
	}
2176
2177 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...
2178
	{
2179
		global $babDB;
2180
2181
		$babDB->db_query("
2182
			UPDATE absences_entries
2183
			SET archived=".$babDB->quote(1)."
2184
			WHERE
2185
				id=".$babDB->quote($this->id)."
2186
		");
2187
	}
2188
2189
2190 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...
2191
	{
2192
		global $babDB;
2193
2194
		if (!$this->id)
2195
		{
2196
			throw new Exception('Missing ID');
2197
		}
2198
2199
		$babDB->db_query("
2200
			UPDATE absences_entries
2201
				SET appr_notified=".$babDB->quote(1)."
2202
			WHERE
2203
				id=".$babDB->quote($this->id)."
2204
		");
2205
	}
2206
2207
2208
	public function getManagerEditUrl()
2209
	{
2210
		$addon = bab_getAddonInfosInstance('absences');
2211
		$ts = bab_mktime($this->date_begin);
2212
		return $addon->getUrl().'vacuser&idx=period&id='.$this->id.'&year='.date('Y', $ts).'&month='.date('n', $ts).'&rfrom=1';
2213
	}
2214
2215
2216
	public function getManagerDeleteUrl()
2217
	{
2218
		$addon = bab_getAddonInfosInstance('absences');
2219
		$url = new bab_url($addon->getUrl().'vacadmb');
2220
		$url->idx = 'delete';
2221
		$url->id_entry = $this->id;
2222
		$url->from = $_SERVER['REQUEST_URI'];
2223
		return $url->toString();
2224
	}
2225
2226
2227
	public function getManagerFrame()
2228
	{
2229
		$W = bab_Widgets();
2230
		$vbox = $W->VBoxLayout();
2231
2232
		if ($this->isFixed())
2233
		{
2234
			$vbox->addItem($W->Icon(absences_translate('Fixed vacation period'), Func_Icons::ACTIONS_VIEW_CALENDAR_DAY));
2235
		} else {
2236
			$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));
2237
		}
2238
		$vbox->addItem($W->Label(absences_DateTimePeriod($this->date_begin, $this->date_end)));
2239
2240
		return $vbox;
2241
	}
2242
2243
2244
	/**
2245
	 *
2246
	 * @param bool 			$display_username	Affiche le nom du demandeur dans la card frame ou non, utile pour les listes contenant plusieurs demandeurs possibles
2247
	 * @param int			$rfrom				si les demandes de la liste sont modifiee par un gestionnaire ou gestionnaire delegue
2248
 	 * @param int			$ide				id entite de l'organigramme >0 si gestionnaire delegue seulement
2249
	 */
2250
	public function getCardFrame($display_username, $rfrom, $ide)
2251
	{
2252
		bab_functionality::includeOriginal('Icons');
2253
2254
		$W = bab_Widgets();
2255
		$layout = $W->HBoxLayout()->setHorizontalSpacing(2,'em')->addClass('widget-full-width');
2256
		$frame = $W->Frame(null, $layout);
2257
2258
		$frame->addClass(Func_Icons::ICON_LEFT_16);
2259
		$frame->addClass('absences-entry-cardframe');
2260
		$layout->addItem($col1 = $W->VBoxLayout()->setVerticalSpacing(.5,'em'));
2261
		$layout->addItem($col2 = $W->VBoxLayout()->setVerticalSpacing(.3,'em'));
2262
2263
		if ($this->isPrevisonal())
2264
		{
2265
			$btn = $W->Link($W->Button()->addItem($W->Label(absences_translate('Submit for approval'))), absences_addon()->getUrl()."vacuser&idx=subprev&id_entry=".$this->id);
2266
			$btn->setSizePolicy('display-opened');
2267
			$layout->addItem($btn);
2268
		}
2269
2270
		if ($this->isFolderPrevisonal())
2271
		{
2272
			$first = $this->getFolderFirst();
2273
			if ($first && $first->id === $this->id)
2274
			{
2275
				$btn = $W->Link($W->Button()->addItem($W->Label(absences_translate('Submit requests for approval'))), absences_addon()->getUrl()."vacuser&idx=subprev&id_entry=".$this->id);
2276
				$btn->setSizePolicy('display-closed');
2277
				$layout->addItem($btn);
2278
			}
2279
		}
2280
2281
2282
		$layout->addItem($col3 = $W->VBoxLayout()->setVerticalSpacing(.5,'em'));
2283
2284
		if ($this->isFixed())
2285
		{
2286
			$col1->addItem($W->Icon(absences_translate('Fixed vacation period'), Func_Icons::ACTIONS_VIEW_CALENDAR_DAY));
2287
			$col1->setCanvasOptions($col1->Options()->width(25,'em'));
2288
		} else {
2289
			$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));
2290
			$col1->setCanvasOptions($col1->Options()->width(25,'em'));
2291
2292
			$col1->addItem($this->getStatusIcon());
2293
		}
2294
2295
		$col2->setSizePolicy(Widget_SizePolicy::MAXIMUM);
2296
2297
		$col2->addItem($W->Title(absences_DateTimePeriod($this->date_begin, $this->date_end), 5));
2298
2299
		if ($display_username)
2300
		{
2301
			$col2->addItem($this->labelledValue(absences_translate('Owner'), $this->getUserName()));
2302
		}
2303
2304
		$col2->addItem($this->labelledValue(absences_translate('Quantity'), absences_vacEntryQuantity($this->id)));
2305
2306
2307
2308
2309 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...
2310
		{
2311
			$col3->addItem($W->Link($W->Icon(absences_translate('Modify'), Func_Icons::ACTIONS_DOCUMENT_EDIT), $this->getEditUrl($rfrom, $ide)));
2312
		}
2313
2314
		if ($this->canDelete())
2315
		{
2316
			$urldelete = absences_addon()->getUrl()."vacuser&idx=delete&id_entry=".$this->id;
2317
			$col3->addItem($W->Link($W->Icon(absences_translate('Delete'), Func_Icons::ACTIONS_EDIT_DELETE), $urldelete));
2318
		}
2319
2320
		$frame->setTitle(sprintf(absences_translate('Created the %s'), bab_shortDate(bab_mktime($this->createdOn()))));
2321
2322
		return $frame;
2323
	}
2324
2325
2326
2327
2328
2329
	/**
2330
	 * @return string
2331
	 */
2332
	public function getEditUrl($rfrom, $ide = null)
2333
	{
2334
		$begin_ts = bab_mktime($this->date_begin);
2335
		$url = absences_addon()->getUrl()."vacuser&idx=period&id=".$this->id."&year=".date('Y',$begin_ts)."&month=".date('n',$begin_ts);
2336
2337
		if (isset($rfrom))
2338
		{
2339
			$url .= '&rfrom='.$rfrom;
2340
		}
2341
2342
		if (isset($ide))
2343
		{
2344
			$url .= '&ide='.$ide;
2345
		}
2346
2347
		return $url;
2348
	}
2349
2350
2351
2352
	/**
2353
	 * Delete the vacation request
2354
	 * @return bool
2355
	 */
2356
	public function delete()
2357
	{
2358
2359
2360
2361
		global $babDB;
2362
2363
		if (!$this->getRow())
2364
		{
2365
			return false;
2366
		}
2367
2368
		parent::delete();
2369
		
2370
		if ('0000-00-00 00:00:00' !== $this->date_begin && '0000-00-00 00:00:00' !== $this->date_end) {
2371
    		include_once $GLOBALS['babInstallPath']."utilit/dateTime.php";
2372
    		$date_begin = BAB_DateTime::fromIsoDateTime($this->date_begin);
2373
    		$date_end	= BAB_DateTime::fromIsoDateTime($this->date_end);
2374
    
2375
    		$period = absences_getPeriod($this->id, $this->id_user, $date_begin, $date_end);
2376
    		if (null !== $period)
2377
    		{
2378
    			$period->delete();
2379
    		}
2380
		}
2381
		$babDB->db_query("DELETE FROM absences_dynamic_rights WHERE id_entry=".$babDB->quote($this->id));
2382
		$babDB->db_query("DELETE FROM ".ABSENCES_ENTRIES_ELEM_TBL." WHERE id_entry=".$babDB->quote($this->id)."");
2383
		$babDB->db_query("DELETE FROM ".ABSENCES_ENTRIES_TBL." WHERE id=".$babDB->quote($this->id));
2384
2385
		$this->applyDynamicRight();
2386
		
2387
		if (isset($date_begin) && isset($date_end)) {
2388
    		$cal_begin = clone $date_begin;
2389
    		$cal_end = clone $date_end;
2390
    
2391
    		$cal_end->add(1, BAB_DATETIME_MONTH);
2392
    
2393
    		while ($cal_begin->getTimeStamp() <= $cal_end->getTimeStamp()) {
2394
    			$month	= $cal_begin->getMonth();
2395
    			$year	= $cal_begin->getYear();
2396
    			absences_updateCalendar($this->id_user, $year, $month);
2397
    			$cal_begin->add(1, BAB_DATETIME_MONTH);
2398
    		}
2399
    		
2400
    		$this->addMovement(
2401
    		        sprintf(
2402
    		                absences_translate('The vacation entry from %s to %s has been deleted'),
2403
    		                $date_begin->shortFormat(true),
2404
    		                $date_end->shortFormat(true)
2405
    		        )
2406
    		);
2407
		}
2408
2409
		
2410
2411
		return true;
2412
	}
2413
2414
2415
2416
2417
2418
2419
2420
	/**
2421
	 * (non-PHPdoc)
2422
	 * @see absences_Request::notifyOwner()
2423
	 */
2424
	public function notifyOwner()
2425
	{
2426
		parent::notifyOwner();
2427
2428
		if ('Y' != $this->status)
2429
		{
2430
			return;
2431
		}
2432
2433
		$agent = $this->getAgent();
2434
		$emails = $agent->getEmails();
2435
2436
		if (empty($emails))
2437
		{
2438
			return;
2439
		}
2440
2441
		absences_notifyEntryOwnerEmails(array($this), $emails);
2442
	}
2443
2444
}
2445
2446
2447
2448
2449
class absences_EntryException extends Exception
2450
{
2451
	/**
2452
	 * Optional entry associated to error (unsaved entry object)
2453
	 * @var absences_Entry
2454
	 */
2455
	public $entry;
2456
2457
2458
	/**
2459
	 * Define if the exception if blocking a save
2460
	 * @var bool
2461
	 */
2462
	public $blocking = true;
2463
}
2464
2465
2466
2467
/**
2468
 * Vacation requests
2469
 * Sorted by creation date
2470
 */
2471
class absences_EntryIterator extends absences_Iterator
2472
{
2473
2474
	/**
2475
	 *
2476
	 * @var string
2477
	 */
2478
	public $orderby = 'createdOn ASC';
2479
2480
2481
	/**
2482
	 *
2483
	 * @var string
2484
	 */
2485
	public $folder;
2486
2487
	/**
2488
	 *
2489
	 * @var array
2490
	 */
2491
	public $users;
2492
2493
2494
	/**
2495
	 *
2496
	 * @var mixed (array|string)
2497
	 */
2498
	public $status;
2499
2500
2501
	/**
2502
	 * Entry to ignore in the folder
2503
	 * @var int
2504
	 */
2505
	public $id_entry_folder;
2506
2507
2508
	/**
2509
	 * Filtrer les demandes necessitant ou pas un email aux approbateurs
2510
	 * @var int
2511
	 */
2512
	public $appr_notified;
2513
2514
	/**
2515
	 * Filtrer les demandes avec unes instance d'approbation
2516
	 * @var bool
2517
	 */
2518
	public $idfai_set;
2519
2520
2521
	/**
2522
	 * Filtrer les demandes avec une liste d'instances d'approbation
2523
	 * @var array
2524
	 */
2525
	public $appr_idfai;
2526
2527
2528
	/**
2529
	 * Search all request created before this date time
2530
	 * @var string
2531
	 */
2532
	public $createdOn;
2533
2534
	/**
2535
	 * Search all request modified before this date
2536
	 * @var string
2537
	 */
2538
	public $modifiedOn;
2539
2540
2541
	/**
2542
	 * Datetime
2543
	 * @var string
2544
	 */
2545
	public $from;
2546
2547
2548
	/**
2549
	 * Datetime
2550
	 * @var string
2551
	 */
2552
	public $to;
2553
2554
2555
	/**
2556
	 * Filtrer les demandes par annee
2557
	 * @var int
2558
	 */
2559
	public $year;
2560
2561
2562
	/**
2563
	 * Filtrer les demandes par organisation
2564
	 * @var int
2565
	 */
2566
	public $organization;
2567
2568
2569
	/**
2570
	 * Filtrer les demandes par statut d'archivage
2571
	 * @var int
2572
	 */
2573
	public $archived = 0;
2574
	
2575
	/**
2576
	 * with at least one element with this right
2577
	 */
2578
	public $id_right;
2579
2580
2581
2582 6
	public function getObject($data)
2583
	{
2584
2585 6
		$entry = new absences_Entry;
2586 6
		$entry->setRow($data);
2587 6
		return $entry;
2588
2589
	}
2590
2591
2592
2593 21
	public function executeQuery()
2594
	{
2595 21
		if(is_null($this->_oResult))
2596 21
		{
2597 21
			global $babDB;
2598
			$req = 'SELECT e.* 
2599
				FROM
2600
					absences_entries e 
2601 21
				';
2602
2603
2604 21
			$where = array();
2605 21
			if (isset($this->folder))
2606 21
			{
2607
				$where[] = 'e.folder='.$babDB->quote($this->folder);
2608
2609
				if (isset($this->id_entry_folder))
2610
				{
2611
					$where[] = 'e.id<>'.$babDB->quote($this->id_entry_folder);
2612
				}
2613
			}
2614
2615 21
			if (!empty($this->users))
2616 21
			{
2617 21
				$where[] = 'e.id_user IN('.$babDB->quote($this->users).')';
2618 21
			}
2619
2620 21
			if (isset($this->status))
2621 21
			{
2622 7
				$where[] = 'e.status IN('.$babDB->quote($this->status).')';
2623 7
			}
2624
2625 21
			if (isset($this->createdOn))
2626 21
			{
2627 15
				$where[] = 'e.createdOn<='.$babDB->quote($this->createdOn);
2628 15
			}
2629
2630 21
			if (isset($this->modifiedOn))
2631 21
			{
2632
				$where[] = 'e.`date`<='.$babDB->quote($this->modifiedOn);
2633
			}
2634
2635 21
			if (isset($this->appr_notified))
2636 21
			{
2637
				$where[] = 'e.appr_notified='.$babDB->quote($this->appr_notified);
2638
			}
2639
2640 21
			if (isset($this->idfai_set) && $this->idfai_set)
2641 21
			{
2642
				$where[] = 'e.idfai>'.$babDB->quote(0);
2643
			}
2644
2645 21
			if (isset($this->appr_idfai))
2646 21
			{
2647
				$where[] = 'e.idfai IN('.$babDB->quote($this->appr_idfai).')';
2648
			}
2649
2650 21
			if (isset($this->from))
2651 21
			{
2652
				$where[] = 'e.date_end >'.$babDB->quote($this->from);
2653
			}
2654
2655 21
			if (isset($this->to))
2656 21
			{
2657
				$where[] = 'e.date_begin <'.$babDB->quote($this->to);
2658
			}
2659
2660 21
			if (isset($this->startTo))
2661 21
			{
2662
				$where[] = 'e.date_begin <='.$babDB->quote($this->startTo);
0 ignored issues
show
Bug introduced by
The property startTo does not exist. Did you maybe forget to declare it?

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

class MyClass { }

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

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

class MyClass {
    public $foo;
}

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

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

class MyClass { }

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

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

class MyClass {
    public $foo;
}

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

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

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

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