Completed
Push — master ( 584969...3f5763 )
by
unknown
11:30
created

createOpportunitiesCountQb()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 21
Code Lines 14

Duplication

Lines 21
Ratio 100 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 21
loc 21
rs 9.0534
cc 4
eloc 14
nc 8
nop 3
1
<?php
2
3
namespace OroCRM\Bundle\SalesBundle\Entity\Repository;
4
5
use DateTime;
6
7
use Doctrine\ORM\EntityRepository;
8
use Doctrine\ORM\QueryBuilder;
9
10
use Oro\Bundle\DataAuditBundle\Loggable\LoggableManager;
11
use Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper;
12
use Oro\Bundle\EntityExtendBundle\Tools\ExtendHelper;
13
use Oro\Bundle\WorkflowBundle\Entity\WorkflowStep;
14
use Oro\Component\DoctrineUtils\ORM\QueryUtils;
15
16
use OroCRM\Bundle\SalesBundle\Entity\Opportunity;
17
18
/**
19
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
20
 */
21
class OpportunityRepository extends EntityRepository
22
{
23
    const OPPORTUNITY_STATE_IN_PROGRESS      = 'In Progress';
24
    const OPPORTUNITY_STATE_IN_PROGRESS_CODE = 'in_progress';
25
26
    /**
27
     * @var WorkflowStep[]
28
     */
29
    protected $workflowStepsByName;
30
31
    /**
32
     * Get opportunities by state by current quarter
33
     *
34
     * @param           $aclHelper AclHelper
35
     * @param  array    $dateRange
36
     * @param  array    $states
37
     * @param int[]     $owners
38
     *
39
     * @param AclHelper $aclHelper
40
     * @param array     $dateRange
41
     * @param array     $states
42
     * @param int[]     $owners
43
     *
44
     * @return array
45
     */
46
    public function getOpportunitiesByStatus(AclHelper $aclHelper, $dateRange, $states, $owners = [])
47
    {
48
        $dateEnd   = $dateRange['end'];
49
        $dateStart = $dateRange['start'];
50
51
        return $this->getOpportunitiesDataByStatus($aclHelper, $dateStart, $dateEnd, $states, $owners);
52
    }
53
54
    /**
55
     * @param string $alias
56
     * @param string $orderBy
57
     * @param string $direction
58
     *
59
     * @return QueryBuilder
60
     *
61
     */
62
    public function getGroupedOpportunitiesByStatusQB(
63
        $alias,
64
        $orderBy = 'budget',
65
        $direction = 'DESC'
66
    ) {
67
        $statusClass = ExtendHelper::buildEnumValueClassName('opportunity_status');
68
        $repository  = $this->getEntityManager()->getRepository($statusClass);
69
70
        $qb = $repository->createQueryBuilder('s')
71
            ->select(
72
                's.name as label',
73
                sprintf('COUNT(%s.id) as quantity', $alias),
74
                // Use close revenue for calculating budget for opportunities with won statuses
75
                sprintf(
76
                    "SUM(
77
                        CASE WHEN s.id = 'won'
78
                            THEN
79
                                (CASE WHEN %s.closeRevenue IS NOT NULL THEN %s.closeRevenue ELSE 0 END)
80
                            ELSE
81
                                (CASE WHEN %s.budgetAmount IS NOT NULL THEN %s.budgetAmount ELSE 0 END)
82
                        END
83
                    ) as budget",
84
                    $alias,
85
                    $alias,
86
                    $alias,
87
                    $alias
88
                )
89
            )
90
            ->leftJoin('OroCRMSalesBundle:Opportunity', $alias, 'WITH', sprintf('%s.status = s', $alias))
91
            ->groupBy('s.name')
92
            ->orderBy($orderBy, $direction);
93
94
        return $qb;
95
    }
96
97
    /**
98
     * @param  AclHelper $aclHelper
99
     * @param            $dateStart
100
     * @param            $dateEnd
101
     * @param array      $states
102
     * @param int[]      $owners
103
     *
104
     * @return array
105
     */
106
    protected function getOpportunitiesDataByStatus(
107
        AclHelper $aclHelper,
108
        $dateStart = null,
109
        $dateEnd = null,
110
        $states = [],
111
        $owners = []
112
    ) {
113
        foreach ($states as $key => $name) {
114
            $resultData[$key] = [
0 ignored issues
show
Coding Style Comprehensibility introduced by
$resultData was never initialized. Although not strictly required by PHP, it is generally a good practice to add $resultData = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
115
                'name'   => $key,
116
                'label'  => $name,
117
                'budget' => 0,
118
            ];
119
        }
120
121
        // select opportunity data
122
        $qb = $this->createQueryBuilder('opportunity');
123
        $qb->select('IDENTITY(opportunity.status) as name, SUM(opportunity.budgetAmount) as budget')
124
            ->groupBy('opportunity.status');
125
126 View Code Duplication
        if ($dateStart && $dateEnd) {
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...
127
            $qb->where($qb->expr()->between('opportunity.createdAt', ':dateFrom', ':dateTo'))
128
                ->setParameter('dateFrom', $dateStart)
129
                ->setParameter('dateTo', $dateEnd);
130
        }
131
132
        if ($owners) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $owners of type integer[] 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...
133
            QueryUtils::applyOptimizedIn($qb, 'opportunity.owner', $owners);
134
        }
135
136
        $groupedData = $aclHelper->apply($qb)->getArrayResult();
137
138
        foreach ($groupedData as $statusData) {
139
            $status = $statusData['name'];
140
            $budget = (float)$statusData['budget'];
141
            if ($budget) {
142
                $resultData[$status]['budget'] = $budget;
0 ignored issues
show
Bug introduced by
The variable $resultData 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...
143
            }
144
        }
145
146
        return $resultData;
147
    }
148
149
    /**
150
     * @param array       $ownerIds
151
     * @param DateTime    $date
152
     * @param AclHelper   $aclHelper
153
     *
154
     * @param string|null $start
155
     * @param string|null $end
156
     *
157
     * @return mixed
158
     */
159
    public function getForecastOfOpportunitiesData($ownerIds, $date, AclHelper $aclHelper, $start = null, $end = null)
160
    {
161
        if ($date === null) {
162
            return $this->getForecastOfOpportunitiesCurrentData($ownerIds, $aclHelper, $start, $end);
163
        }
164
165
        return $this->getForecastOfOpportunitiesOldData($ownerIds, $date, $aclHelper);
166
    }
167
168
    /**
169
     * @param array       $ownerIds
170
     * @param AclHelper   $aclHelper
171
     * @param string|null $start
172
     * @param string|null $end
173
     *
174
     * @return mixed
175
     */
176
    protected function getForecastOfOpportunitiesCurrentData(
177
        $ownerIds,
178
        AclHelper $aclHelper,
179
        $start = null,
180
        $end = null
181
    ) {
182
        $qb = $this->createQueryBuilder('opportunity');
183
184
        $select = "
185
            COUNT( opportunity.id ) as inProgressCount,
186
            SUM( opportunity.budgetAmount ) as budgetAmount,
187
            SUM( opportunity.budgetAmount * opportunity.probability ) as weightedForecast";
188
        $qb
189
            ->select($select)
190
            ->andWhere('opportunity.status NOT IN (:notCountedStatuses)')
191
            ->setParameter('notCountedStatuses', ['lost', 'won']);
192
        if (!empty($ownerIds)) {
193
            $qb->join('opportunity.owner', 'owner');
194
            QueryUtils::applyOptimizedIn($qb, 'owner.id', $ownerIds);
195
        }
196
197
        $probabilityCondition = $qb->expr()->orX(
198
            $qb->expr()->andX(
199
                'opportunity.probability <> 0',
200
                'opportunity.probability <> 1'
201
            ),
202
            'opportunity.probability is NULL'
203
        );
204
205
        $qb->andWhere($probabilityCondition);
206
        if ($start) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $start of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
207
            $qb
208
                ->andWhere('opportunity.closeDate >= :startDate')
209
                ->setParameter('startDate', $start);
210
        }
211
        if ($end) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $end of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
212
            $qb
213
                ->andWhere('opportunity.closeDate <= :endDate')
214
                ->setParameter('endDate', $end);
215
        }
216
217
        return $aclHelper->apply($qb)->getOneOrNullResult();
218
    }
219
220
    /**
221
     * @param array     $ownerIds
222
     * @param \DateTime $date
223
     * @param AclHelper $aclHelper
224
     *
225
     * @return mixed
226
     */
227
    protected function getForecastOfOpportunitiesOldData($ownerIds, $date, AclHelper $aclHelper)
228
    {
229
        //clone date for avoiding wrong date on printing with current locale
230
        $newDate = clone $date;
231
        $qb      = $this->createQueryBuilder('opportunity')
232
            ->where('opportunity.createdAt < :date')
233
            ->setParameter('date', $newDate);
234
235
        $opportunities = $aclHelper->apply($qb)->getResult();
236
237
        $result['inProgressCount']  = 0;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$result was never initialized. Although not strictly required by PHP, it is generally a good practice to add $result = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
238
        $result['budgetAmount']     = 0;
239
        $result['weightedForecast'] = 0;
240
241
        $auditRepository = $this->getEntityManager()->getRepository('OroDataAuditBundle:Audit');
242
        /** @var Opportunity $opportunity */
243
        foreach ($opportunities as $opportunity) {
244
            $auditQb = $auditRepository->getLogEntriesQueryBuilder($opportunity);
245
            $auditQb->andWhere('a.action = :action')
246
                ->andWhere('a.loggedAt > :date')
247
                ->setParameter('action', LoggableManager::ACTION_UPDATE)
248
                ->setParameter('date', $newDate);
249
            $opportunityHistory = $aclHelper->apply($auditQb)->getResult();
250
251
            if ($oldProbability = $this->getHistoryOldValue($opportunityHistory, 'probability')) {
252
                $isProbabilityOk = $oldProbability !== 0 && $oldProbability !== 1;
253
                $probability     = $oldProbability;
254
            } else {
255
                $probability     = $opportunity->getProbability();
256
                $isProbabilityOk = !is_null($probability) && $probability !== 0 && $probability !== 1;
257
            }
258
259
            if ($isProbabilityOk
260
                && $this->isOwnerOk($ownerIds, $opportunityHistory, $opportunity)
261
                && $this->isStatusOk($opportunityHistory, $opportunity)
262
            ) {
263
                $result = $this->calculateOpportunityOldValue($result, $opportunityHistory, $opportunity, $probability);
264
            }
265
        }
266
267
        return $result;
268
    }
269
270
    /**
271
     * @param mixed  $opportunityHistory
272
     * @param string $field
273
     *
274
     * @return mixed
275
     */
276
    protected function getHistoryOldValue($opportunityHistory, $field)
277
    {
278
        $result = null;
279
280
        $opportunityHistory = is_array($opportunityHistory) ? $opportunityHistory : [$opportunityHistory];
281
        foreach ($opportunityHistory as $item) {
282
            if ($item->getField($field)) {
283
                $result = $item->getField($field)->getOldValue();
284
            }
285
        }
286
287
        return $result;
288
    }
289
290
    /**
291
     * @param array       $opportunityHistory
292
     * @param Opportunity $opportunity
293
     *
294
     * @return bool
295
     */
296
    protected function isStatusOk($opportunityHistory, $opportunity)
297
    {
298
        if ($oldStatus = $this->getHistoryOldValue($opportunityHistory, 'status')) {
299
            $isStatusOk = $oldStatus === self::OPPORTUNITY_STATE_IN_PROGRESS;
300
        } else {
301
            $isStatusOk = $opportunity->getStatus()->getName() === self::OPPORTUNITY_STATE_IN_PROGRESS_CODE;
0 ignored issues
show
Bug introduced by
The method getStatus() does not seem to exist on object<OroCRM\Bundle\Sal...dle\Entity\Opportunity>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
302
        }
303
304
        return $isStatusOk;
305
    }
306
307
    /**
308
     * @param array       $ownerIds
309
     * @param array       $opportunityHistory
310
     * @param Opportunity $opportunity
311
     *
312
     * @return bool
313
     */
314
    protected function isOwnerOk($ownerIds, $opportunityHistory, $opportunity)
315
    {
316
        $userRepository = $this->getEntityManager()->getRepository('OroUserBundle:User');
317
        if ($oldOwner = $this->getHistoryOldValue($opportunityHistory, 'owner')) {
318
            $isOwnerOk = in_array($userRepository->findOneByUsername($oldOwner)->getId(), $ownerIds);
319
        } else {
320
            $isOwnerOk = in_array($opportunity->getOwner()->getId(), $ownerIds);
321
        }
322
323
        return $isOwnerOk;
324
    }
325
326
    /**
327
     * @param array       $result
328
     * @param array       $opportunityHistory
329
     * @param Opportunity $opportunity
330
     * @param mixed       $probability
331
     *
332
     * @return array
333
     */
334
    protected function calculateOpportunityOldValue($result, $opportunityHistory, $opportunity, $probability)
335
    {
336
        ++$result['inProgressCount'];
337
        $oldBudgetAmount = $this->getHistoryOldValue($opportunityHistory, 'budgetAmount');
338
339
        $budget = $oldBudgetAmount !== null ? $oldBudgetAmount : $opportunity->getBudgetAmount();
340
        $result['budgetAmount'] += $budget;
341
        $result['weightedForecast'] += $budget * $probability;
342
343
        return $result;
344
    }
345
346
    /**
347
     * @param AclHelper $aclHelper
348
     * @param DateTime  $start
349
     * @param DateTime  $end
350
     * @param int[]     $owners
351
     *
352
     * @return int
353
     */
354 View Code Duplication
    public function getOpportunitiesCount(
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...
355
        AclHelper $aclHelper,
356
        DateTime $start = null,
357
        DateTime $end = null,
358
        $owners = []
359
    ) {
360
        $qb = $this->createOpportunitiesCountQb($start, $end, $owners);
361
362
        return $aclHelper->apply($qb)->getSingleScalarResult();
363
    }
364
365
    /**
366
     * @param AclHelper $aclHelper
367
     * @param DateTime  $start
368
     * @param DateTime  $end
369
     * @param int[]     $owners
370
     *
371
     * @return int
372
     */
373 View Code Duplication
    public function getNewOpportunitiesCount(
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...
374
        AclHelper $aclHelper,
375
        DateTime $start = null,
376
        DateTime $end = null,
377
        $owners = []
378
    ) {
379
        $qb = $this->createOpportunitiesCountQb($start, $end, $owners)
380
            ->andWhere('o.closeDate IS NULL');
381
382
        return $aclHelper->apply($qb)->getSingleScalarResult();
383
    }
384
385
    /**
386
     * @param DateTime $start
387
     * @param DateTime $end
388
     * @param int[]    $owners
389
     *
390
     * @return QueryBuilder
391
     */
392 View Code Duplication
    public function createOpportunitiesCountQb(DateTime $start = null, DateTime $end = null, $owners = [])
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...
393
    {
394
        $qb = $this->createQueryBuilder('o');
395
        $qb->select('COUNT(o.id)');
396
        if ($start) {
397
            $qb
398
                ->andWhere('o.createdAt > :start')
399
                ->setParameter('start', $start);
400
        }
401
        if ($end) {
402
            $qb
403
                ->andWhere('o.createdAt < :end')
404
                ->setParameter('end', $end);
405
        }
406
407
        if ($owners) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $owners of type integer[] 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...
408
            QueryUtils::applyOptimizedIn($qb, 'o.owner', $owners);
409
        }
410
411
        return $qb;
412
    }
413
414
    /**
415
     * @param AclHelper $aclHelper
416
     * @param DateTime  $start
417
     * @param DateTime  $end
418
     * @param int[]     $owners
419
     *
420
     * @return double
421
     */
422 View Code Duplication
    public function getTotalServicePipelineAmount(
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...
423
        AclHelper $aclHelper,
424
        DateTime $start = null,
425
        DateTime $end = null,
426
        $owners = []
427
    ) {
428
        $qb = $this->createQueryBuilder('o');
429
430
        $qb
431
            ->select('SUM(o.budgetAmount)')
432
            ->andWhere('o.closeDate IS NULL')
433
            ->andWhere('o.status = :status')
434
            ->andWhere('o.probability != 0')
435
            ->andWhere('o.probability != 1')
436
            ->setParameter('status', self::OPPORTUNITY_STATE_IN_PROGRESS_CODE);
437
        if ($start) {
438
            $qb
439
                ->andWhere('o.createdAt > :start')
440
                ->setParameter('start', $start);
441
        }
442
        if ($end) {
443
            $qb
444
                ->andWhere('o.createdAt < :end')
445
                ->setParameter('end', $end);
446
        }
447
448
        if ($owners) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $owners of type integer[] 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...
449
            QueryUtils::applyOptimizedIn($qb, 'o.owner', $owners);
450
        }
451
452
        return $aclHelper->apply($qb)->getSingleScalarResult();
453
    }
454
455
    /**
456
     * @param AclHelper $aclHelper
457
     * @param DateTime  $start
458
     * @param DateTime  $end
459
     *
460
     * @return double
461
     */
462
    public function getTotalServicePipelineAmountInProgress(
463
        AclHelper $aclHelper,
464
        DateTime $start = null,
465
        DateTime $end = null
466
    ) {
467
        $qb = $this->createQueryBuilder('o');
468
469
        $qb
470
            ->select('SUM(o.budgetAmount)')
471
            ->andWhere('o.status = :status')
472
            ->andWhere('o.probability != 0')
473
            ->andWhere('o.probability != 1')
474
            ->setParameter('status', self::OPPORTUNITY_STATE_IN_PROGRESS_CODE);
475
        if ($start) {
476
            $qb
477
                ->andWhere('o.createdAt > :start')
478
                ->setParameter('start', $start);
479
        }
480
        if ($end) {
481
            $qb
482
                ->andWhere('o.createdAt < :end')
483
                ->setParameter('end', $end);
484
        }
485
486
        return $aclHelper->apply($qb)->getSingleScalarResult();
487
    }
488
489
    /**
490
     * @param AclHelper $aclHelper
491
     * @param DateTime  $start
492
     * @param DateTime  $end
493
     *
494
     * @return double
495
     */
496 View Code Duplication
    public function getWeightedPipelineAmount(AclHelper $aclHelper, DateTime $start = null, DateTime $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...
497
    {
498
        $qb = $this->createQueryBuilder('o');
499
500
        $qb->select('SUM(o.budgetAmount * o.probability)');
501
        if ($start) {
502
            $qb
503
                ->andWhere('o.createdAt > :start')
504
                ->setParameter('start', $start);
505
        }
506
        if ($end) {
507
            $qb
508
                ->andWhere('o.createdAt < :end')
509
                ->setParameter('end', $end);
510
        }
511
512
        return $aclHelper->apply($qb)->getSingleScalarResult();
513
    }
514
515
    /**
516
     * @param AclHelper $aclHelper
517
     * @param DateTime  $start
518
     * @param DateTime  $end
519
     * @param int[]     $owners
520
     *
521
     * @return double
522
     */
523 View Code Duplication
    public function getOpenWeightedPipelineAmount(
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...
524
        AclHelper $aclHelper,
525
        DateTime $start = null,
526
        DateTime $end = null,
527
        $owners = []
528
    ) {
529
        $qb = $this->createQueryBuilder('o');
530
531
        $qb
532
            ->select('SUM(o.budgetAmount * o.probability)')
533
            ->andWhere('o.status = :status')
534
            ->andWhere('o.probability != 0')
535
            ->andWhere('o.probability != 1')
536
            ->setParameter('status', self::OPPORTUNITY_STATE_IN_PROGRESS_CODE);
537
        if ($start) {
538
            $qb
539
                ->andWhere('o.createdAt > :start')
540
                ->setParameter('start', $start);
541
        }
542
        if ($end) {
543
            $qb
544
                ->andWhere('o.createdAt < :end')
545
                ->setParameter('end', $end);
546
        }
547
548
        if ($owners) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $owners of type integer[] 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...
549
            QueryUtils::applyOptimizedIn($qb, 'o.owner', $owners);
550
        }
551
552
        return $aclHelper->apply($qb)->getSingleScalarResult();
553
    }
554
}
555