1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace OroCRM\Bundle\SalesBundle\Dashboard\Provider; |
4
|
|
|
|
5
|
|
|
use Symfony\Bridge\Doctrine\RegistryInterface; |
6
|
|
|
|
7
|
|
|
use Oro\Component\DoctrineUtils\ORM\QueryUtils; |
8
|
|
|
|
9
|
|
|
use Oro\Bundle\DashboardBundle\Filter\DateFilterProcessor; |
10
|
|
|
use Oro\Bundle\DashboardBundle\Model\WidgetOptionBag; |
11
|
|
|
use Oro\Bundle\EntityExtendBundle\Entity\Repository\EnumValueRepository; |
12
|
|
|
use Oro\Bundle\EntityExtendBundle\Tools\ExtendHelper; |
13
|
|
|
use Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper; |
14
|
|
|
use Oro\Bundle\UserBundle\Dashboard\OwnerHelper; |
15
|
|
|
|
16
|
|
|
use OroCRM\Bundle\SalesBundle\Entity\Repository\OpportunityRepository; |
17
|
|
|
|
18
|
|
|
class OpportunityByStatusProvider |
19
|
|
|
{ |
20
|
|
|
/** @var RegistryInterface */ |
21
|
|
|
protected $registry; |
22
|
|
|
|
23
|
|
|
/** @var AclHelper */ |
24
|
|
|
protected $aclHelper; |
25
|
|
|
|
26
|
|
|
/** @var DateFilterProcessor */ |
27
|
|
|
protected $dateFilterProcessor; |
28
|
|
|
|
29
|
|
|
/** @var OwnerHelper */ |
30
|
|
|
protected $ownerHelper; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @param RegistryInterface $doctrine |
34
|
|
|
* @param AclHelper $aclHelper |
35
|
|
|
* @param DateFilterProcessor $processor |
36
|
|
|
* @param OwnerHelper $ownerHelper |
37
|
|
|
*/ |
38
|
|
|
public function __construct( |
39
|
|
|
RegistryInterface $doctrine, |
40
|
|
|
AclHelper $aclHelper, |
41
|
|
|
DateFilterProcessor $processor, |
42
|
|
|
OwnerHelper $ownerHelper |
43
|
|
|
) { |
44
|
|
|
$this->registry = $doctrine; |
45
|
|
|
$this->aclHelper = $aclHelper; |
46
|
|
|
$this->dateFilterProcessor = $processor; |
47
|
|
|
$this->ownerHelper = $ownerHelper; |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @param WidgetOptionBag $widgetOptions |
52
|
|
|
* |
53
|
|
|
* @return array |
54
|
|
|
*/ |
55
|
|
|
public function getOpportunitiesGroupedByStatus(WidgetOptionBag $widgetOptions) |
56
|
|
|
{ |
57
|
|
|
$dateRange = $widgetOptions->get('dateRange'); |
58
|
|
|
$owners = $this->ownerHelper->getOwnerIds($widgetOptions); |
59
|
|
|
/** |
60
|
|
|
* Excluded statuses will be filtered from result in method `formatResult` below. |
61
|
|
|
* Due to performance issues with `NOT IN` clause in database. |
62
|
|
|
*/ |
63
|
|
|
$excludedStatuses = $widgetOptions->get('excluded_statuses', []); |
64
|
|
|
$orderBy = $widgetOptions->get('useQuantityAsData') ? 'quantity' : 'budget'; |
65
|
|
|
|
66
|
|
|
/** @var OpportunityRepository $opportunityRepository */ |
67
|
|
|
$opportunityRepository = $this->registry->getRepository('OroCRMSalesBundle:Opportunity'); |
68
|
|
|
$qb = $opportunityRepository->createQueryBuilder('o') |
69
|
|
|
->select('IDENTITY (o.status) status') |
70
|
|
|
->groupBy('status') |
71
|
|
|
->orderBy($orderBy, 'DESC'); |
72
|
|
|
|
73
|
|
|
switch ($orderBy) { |
74
|
|
|
case 'quantity': |
75
|
|
|
$qb->addSelect('COUNT(o.id) as quantity'); |
76
|
|
|
break; |
77
|
|
|
case 'budget': |
78
|
|
|
$qb->addSelect( |
79
|
|
|
'SUM( |
80
|
|
|
CASE WHEN o.status = \'won\' |
81
|
|
|
THEN (CASE WHEN o.closeRevenue IS NOT NULL THEN o.closeRevenue ELSE 0 END) |
82
|
|
|
ELSE (CASE WHEN o.budgetAmount IS NOT NULL THEN o.budgetAmount ELSE 0 END) |
83
|
|
|
END |
84
|
|
|
) as budget' |
85
|
|
|
); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
$this->dateFilterProcessor->applyDateRangeFilterToQuery($qb, $dateRange, 'o.createdAt'); |
89
|
|
|
|
90
|
|
|
if ($owners) { |
|
|
|
|
91
|
|
|
QueryUtils::applyOptimizedIn($qb, 'o.owner', $owners); |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
$result = $this->aclHelper->apply($qb)->getArrayResult(); |
95
|
|
|
|
96
|
|
|
return $this->formatResult($result, $excludedStatuses, $orderBy); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* @param array $result |
101
|
|
|
* @param string[] $excludedStatuses |
102
|
|
|
* @param string $orderBy |
103
|
|
|
* |
104
|
|
|
* @return array |
105
|
|
|
*/ |
106
|
|
|
protected function formatResult($result, $excludedStatuses, $orderBy) |
107
|
|
|
{ |
108
|
|
|
$resultStatuses = array_flip(array_column($result, 'status', null)); |
109
|
|
|
|
110
|
|
|
foreach ($this->getAvailableOpportunityStatuses() as $statusKey => $statusLabel) { |
111
|
|
|
$resultIndex = isset($resultStatuses[$statusKey]) ? $resultStatuses[$statusKey] : null; |
112
|
|
|
if (in_array($statusKey, $excludedStatuses)) { |
113
|
|
|
if (null !== $resultIndex) { |
114
|
|
|
unset($result[$resultIndex]); |
115
|
|
|
} |
116
|
|
|
continue; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
if (null !== $resultIndex) { |
120
|
|
|
$result[$resultIndex]['label'] = $statusLabel; |
121
|
|
|
} else { |
122
|
|
|
$result[] = [ |
123
|
|
|
'status' => $statusKey, |
124
|
|
|
'label' => $statusLabel, |
125
|
|
|
$orderBy => 0 |
126
|
|
|
]; |
127
|
|
|
} |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
return $result; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* @return array |
135
|
|
|
*/ |
136
|
|
|
protected function getAvailableOpportunityStatuses() |
137
|
|
|
{ |
138
|
|
|
/** @var EnumValueRepository $statusesRepository */ |
139
|
|
|
$statusesRepository = $this->registry->getRepository( |
140
|
|
|
ExtendHelper::buildEnumValueClassName('opportunity_status') |
141
|
|
|
); |
142
|
|
|
$statuses = $statusesRepository->createQueryBuilder('s') |
143
|
|
|
->select('s.id, s.name') |
144
|
|
|
->getQuery() |
145
|
|
|
->getArrayResult(); |
146
|
|
|
|
147
|
|
|
return array_column($statuses, 'name', 'id'); |
148
|
|
|
} |
149
|
|
|
} |
150
|
|
|
|
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.