Completed
Push — 1.9 ( 66be26...482137 )
by
unknown
62:03
created

ForecastOfOpportunities::getOwnersValues()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 11
rs 9.4285
cc 2
eloc 7
nc 2
nop 2
1
<?php
2
3
namespace OroCRM\Bundle\SalesBundle\Provider;
4
5
use Symfony\Bridge\Doctrine\RegistryInterface;
6
use Symfony\Component\Translation\TranslatorInterface;
7
8
use Oro\Bundle\DashboardBundle\Model\WidgetOptionBag;
9
use Oro\Bundle\LocaleBundle\Formatter\DateTimeFormatter;
10
use Oro\Bundle\LocaleBundle\Formatter\NumberFormatter;
11
use Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper;
12
use Oro\Component\DoctrineUtils\ORM\QueryUtils;
13
14
/**
15
 * Class ForecastOfOpportunities
16
 * @package OroCRM\Bundle\SalesBundle\Provider
17
 */
18
class ForecastOfOpportunities
19
{
20
    /** @var RegistryInterface */
21
    protected $doctrine;
22
23
    /** @var NumberFormatter */
24
    protected $numberFormatter;
25
26
    /** @var DateTimeFormatter */
27
    protected $dateTimeFormatter;
28
29
    /** @var AclHelper */
30
    protected $aclHelper;
31
32
    /** @var TranslatorInterface */
33
    protected $translator;
34
35
    /** @var  array */
36
    protected $ownersValues;
37
38
    /** @var array */
39
    protected $ownerIds = [];
40
41
    /**
42
     * @param RegistryInterface   $doctrine
43
     * @param NumberFormatter     $numberFormatter
44
     * @param DateTimeFormatter   $dateTimeFormatter
45
     * @param AclHelper           $aclHelper
46
     * @param TranslatorInterface $translator
47
     */
48
    public function __construct(
49
        RegistryInterface $doctrine,
50
        NumberFormatter $numberFormatter,
51
        DateTimeFormatter $dateTimeFormatter,
52
        AclHelper $aclHelper,
53
        TranslatorInterface $translator
54
    ) {
55
        $this->doctrine          = $doctrine;
56
        $this->numberFormatter   = $numberFormatter;
57
        $this->dateTimeFormatter = $dateTimeFormatter;
58
        $this->aclHelper         = $aclHelper;
59
        $this->translator        = $translator;
60
    }
61
62
    /**
63
     * @param WidgetOptionBag $widgetOptions
64
     * @param string          $getterName
65
     * @param string          $dataType
66
     * @param bool            $lessIsBetter
67
     * @return array
68
     */
69
    public function getForecastOfOpportunitiesValues(
70
        WidgetOptionBag $widgetOptions,
71
        $getterName,
72
        $dataType,
73
        $lessIsBetter = false
74
    ) {
75
        $lessIsBetter     = (bool)$lessIsBetter;
76
        $result           = [];
77
78
        $ownerIds         = $this->getOwnerIds($widgetOptions);
79
        $value            = $this->{$getterName}($ownerIds);
80
        $result['value']  = $this->formatValue($value, $dataType);
81
        $compareToDate = $widgetOptions->get('compareToDate');
82
83
        if (!empty($compareToDate['useDate'])) {
84
            if (empty($compareToDate['date'])) {
85
                $compareToDate['date'] = new \DateTime();
86
                $compareToDate['date']->modify('-1 month');
87
                $compareToDate['date']->setTime(0, 0, 0);
88
            }
89
            $pastResult = $this->{$getterName}($ownerIds, $compareToDate['date']);
90
            $result['deviation'] = $this->translator
91
                ->trans('orocrm.sales.dashboard.forecast_of_opportunities.no_changes');
92
            $result = $this->prepareData($dataType, $lessIsBetter, $pastResult, $value - $pastResult, $result);
93
            $result['previousRange'] = $this->dateTimeFormatter->formatDate($compareToDate['date']);
94
        }
95
96
        return $result;
97
    }
98
99
    /**
100
     * @param array $ownerIds
101
     * @param null $compareToDate
102
     * @return int
103
     */
104
    protected function getInProgressValues($ownerIds, $compareToDate = null)
105
    {
106
        $values = $this->getOwnersValues($ownerIds, $compareToDate);
107
108
        return $values && isset($values['inProgressCount']) ? $values['inProgressCount'] : 0;
109
    }
110
111
    /**
112
     * @param array $ownerIds
113
     * @param null $compareToDate
114
     * @return int
115
     */
116
    protected function getTotalForecastValues($ownerIds, $compareToDate = null)
117
    {
118
        $values = $this->getOwnersValues($ownerIds, $compareToDate);
119
120
        return $values && isset($values['budgetAmount']) ? $values['budgetAmount'] : 0;
121
    }
122
123
    /**
124
     * @param array $ownerIds
125
     * @param null $compareToDate
126
     * @return int
127
     */
128
    protected function getWeightedForecastValues($ownerIds, $compareToDate = null)
129
    {
130
        $values = $this->getOwnersValues($ownerIds, $compareToDate);
131
132
        return $values && isset($values['weightedForecast']) ? $values['weightedForecast'] : 0;
133
    }
134
135
    /**
136
     * @param array $ownerIds
137
     * @param \DateTime|string|int $date
138
     * @return mixed
139
     */
140
    protected function getOwnersValues(array $ownerIds, $date)
141
    {
142
        $key = sha1(implode('_', $ownerIds) . $this->dateTimeFormatter->formatDate($date));
143
        if (!isset($this->ownersValues[$key])) {
144
            $this->ownersValues[$key] = $this->doctrine
145
                ->getRepository('OroCRMSalesBundle:Opportunity')
146
                ->getForecastOfOpporunitiesData($ownerIds, $date, $this->aclHelper);
147
        }
148
149
        return $this->ownersValues[$key];
150
    }
151
152
    /**
153
     * @param mixed  $value
154
     * @param string $type
155
     * @param bool   $isDeviant
156
     *
157
     * @return string
158
     */
159
    protected function formatValue($value, $type = '', $isDeviant = false)
160
    {
161
        $sign = null;
162
        $precision = 2;
163
164
        if ($isDeviant) {
165
            if ($value !== 0) {
166
                $sign  = $value > 0 ? '+' : '&minus;';
167
                $value = abs($value);
168
            }
169
            $precision = 0;
170
        }
171
172
        if ($type === 'currency') {
173
            $formattedValue = $this->numberFormatter->formatCurrency($value);
174
        } elseif ($type === 'percent') {
175
            $value = round(($value) * 100, $precision) / 100;
176
            $formattedValue = $this->numberFormatter->formatPercent($value);
177
        } else {
178
            $formattedValue = $this->numberFormatter->formatDecimal($value);
179
        }
180
181
        if ($sign) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $sign 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...
182
            $formattedValue = sprintf('%s%s', $sign, $formattedValue);
183
        }
184
        return $formattedValue;
185
    }
186
187
    /**
188
     * @param $dataType
189
     * @param $lessIsBetter
190
     * @param $pastResult
191
     * @param $deviation
192
     * @param $result
193
     *
194
     * @return array
195
     */
196
    protected function prepareData($dataType, $lessIsBetter, $pastResult, $deviation, $result)
197
    {
198
        if ($pastResult != 0 && $dataType !== 'percent') {
199
            if ($deviation != 0) {
200
                $deviationPercent = $deviation / $pastResult;
201
                $result['deviation'] = sprintf(
202
                    '%s (%s)',
203
                    $this->formatValue($deviation, $dataType, true),
204
                    $this->formatValue($deviationPercent, 'percent', true)
205
                );
206
                $result['isPositive'] = $this->isPositive($lessIsBetter, $deviation);
207
            }
208
        } else {
209
            if (round(($deviation) * 100, 0) != 0) {
210
                $result['deviation'] = $this->formatValue($deviation, $dataType, true);
211
                $result['isPositive'] = $this->isPositive($lessIsBetter, $deviation);
212
            }
213
        }
214
215
        return $result;
216
    }
217
218
    /**
219
     * Get is positive value
220
     *
221
     * @param $lessIsBetter
222
     * @param $deviation
223
     *
224
     * @return bool
225
     */
226
    protected function isPositive($lessIsBetter, $deviation)
227
    {
228
        if (!$lessIsBetter) {
229
            $result = $deviation > 0;
230
        } else {
231
            $result = !($deviation > 0);
232
        }
233
234
        return $result;
235
    }
236
237
    /**
238
     * @param WidgetOptionBag $widgetOptions
239
     *
240
     * @return array
241
     */
242
    protected function getOwnerIds(WidgetOptionBag $widgetOptions)
243
    {
244
        $key = spl_object_hash($widgetOptions);
245
        if (!isset($this->ownerIds[$key])) {
246
            $owners = $widgetOptions->get('owners');
247
            $owners = is_array($owners) ? $owners : [$owners];
248
249
            $ownerIds = [];
250
            foreach ($owners as $owner) {
251
                if (is_object($owner)) {
252
                    $ownerIds[] = $owner->getId();
253
                }
254
            }
255
256
            $businessUnitIds = $this->getBusinessUnitsIds($widgetOptions);
257
258
            $this->ownerIds[$key] = array_unique(array_merge($this->getUserOwnerIds($businessUnitIds), $ownerIds));
259
        }
260
261
        return $this->ownerIds[$key];
262
    }
263
264
    /**
265
     * @param int[] $businessUnitIds
266
     *
267
     * @return int[]
268
     */
269
    protected function getUserOwnerIds(array $businessUnitIds)
270
    {
271
        if (!$businessUnitIds) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $businessUnitIds 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...
272
            return [];
273
        }
274
275
        $qb = $this->doctrine->getRepository('OroUserBundle:User')
276
            ->createQueryBuilder('u');
277
278
        $qb
279
            ->select('DISTINCT(u.id)')
280
            ->join('u.businessUnits', 'bu');
281
        QueryUtils::applyOptimizedIn($qb, 'bu.id', $businessUnitIds);
282
283
        return array_map('current', $qb->getQuery()->getResult());
284
    }
285
286
    /**
287
     * @param WidgetOptionBag $widgetOptions
288
     *
289
     * @return array
290
     */
291
    protected function getBusinessUnitsIds(WidgetOptionBag $widgetOptions)
292
    {
293
        $businessUnits = $widgetOptions->get('businessUnits');
294
295
        $businessUnits = is_array($businessUnits) ? $businessUnits : [$businessUnits];
296
297
        $businessUnitIds = [];
298
299
        foreach ($businessUnits as $businessUnit) {
300
            if (is_object($businessUnit)) {
301
                $businessUnitIds[] = $businessUnit->getId();
302
            }
303
        }
304
305
        return $businessUnitIds;
306
    }
307
}
308