|
4 | 4 |
|
5 | 5 | use Symfony\Bridge\Doctrine\RegistryInterface;
|
6 | 6 |
|
7 |
| -use Doctrine\ORM\Query\Expr as Expr; |
| 7 | +use Oro\Component\DoctrineUtils\ORM\QueryUtils; |
8 | 8 |
|
9 | 9 | use Oro\Bundle\DashboardBundle\Filter\DateFilterProcessor;
|
10 | 10 | use Oro\Bundle\DashboardBundle\Model\WidgetOptionBag;
|
| 11 | +use Oro\Bundle\EntityExtendBundle\Entity\Repository\EnumValueRepository; |
| 12 | +use Oro\Bundle\EntityExtendBundle\Tools\ExtendHelper; |
11 | 13 | use Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper;
|
12 | 14 | use Oro\Bundle\UserBundle\Dashboard\OwnerHelper;
|
13 |
| -use Oro\Component\DoctrineUtils\ORM\QueryUtils; |
| 15 | + |
14 | 16 | use OroCRM\Bundle\SalesBundle\Entity\Repository\OpportunityRepository;
|
15 | 17 |
|
16 | 18 | class OpportunityByStatusProvider
|
@@ -54,57 +56,94 @@ public function getOpportunitiesGroupedByStatus(WidgetOptionBag $widgetOptions)
|
54 | 56 | {
|
55 | 57 | $dateRange = $widgetOptions->get('dateRange');
|
56 | 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 | + */ |
57 | 63 | $excludedStatuses = $widgetOptions->get('excluded_statuses', []);
|
58 | 64 | $orderBy = $widgetOptions->get('useQuantityAsData') ? 'quantity' : 'budget';
|
59 |
| - $qb = $this->getOpportunityRepository() |
60 |
| - ->getGroupedOpportunitiesByStatusQB('o', $orderBy); |
| 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 | + } |
61 | 87 |
|
62 | 88 | $this->dateFilterProcessor->applyDateRangeFilterToQuery($qb, $dateRange, 'o.createdAt');
|
63 | 89 |
|
64 | 90 | if ($owners) {
|
65 | 91 | QueryUtils::applyOptimizedIn($qb, 'o.owner', $owners);
|
66 | 92 | }
|
67 | 93 |
|
68 |
| - // move previously applied conditions into join |
69 |
| - // since we don't want to exclude any statuses from result |
70 |
| - $joinConditions = $qb->getDQLPart('where'); |
71 |
| - if ($joinConditions) { |
72 |
| - $whereParts = (string) $joinConditions; |
73 |
| - $qb->resetDQLPart('where'); |
74 |
| - |
75 |
| - $join = $qb->getDQLPart('join')['s'][0]; |
76 |
| - $qb->resetDQLPart('join'); |
77 |
| - |
78 |
| - $qb->add( |
79 |
| - 'join', |
80 |
| - [ |
81 |
| - 's' => new Expr\Join( |
82 |
| - $join->getJoinType(), |
83 |
| - $join->getJoin(), |
84 |
| - $join->getAlias(), |
85 |
| - $join->getConditionType(), |
86 |
| - sprintf('%s AND (%s)', $join->getCondition(), $whereParts), |
87 |
| - $join->getIndexBy() |
88 |
| - ) |
89 |
| - ], |
90 |
| - true |
91 |
| - ); |
92 |
| - } |
| 94 | + $result = $this->aclHelper->apply($qb)->getArrayResult(); |
| 95 | + |
| 96 | + return $this->formatResult($result, $excludedStatuses, $orderBy); |
| 97 | + } |
93 | 98 |
|
94 |
| - if ($excludedStatuses) { |
95 |
| - $qb->andWhere( |
96 |
| - $qb->expr()->notIn('s.id', $excludedStatuses) |
97 |
| - ); |
| 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 | + } |
98 | 128 | }
|
99 | 129 |
|
100 |
| - return $this->aclHelper->apply($qb)->getArrayResult(); |
| 130 | + return $result; |
101 | 131 | }
|
102 | 132 |
|
103 | 133 | /**
|
104 |
| - * @return OpportunityRepository |
| 134 | + * @return array |
105 | 135 | */
|
106 |
| - protected function getOpportunityRepository() |
| 136 | + protected function getAvailableOpportunityStatuses() |
107 | 137 | {
|
108 |
| - return $this->registry->getRepository('OroCRMSalesBundle:Opportunity'); |
| 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'); |
109 | 148 | }
|
110 | 149 | }
|
0 commit comments