Passed
Pull Request — developer (#17136)
by Arkadiusz
35:03 queued 17:53
created

Settings_CurrencyUpdate_Module_Model   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 323
Duplicated Lines 0 %

Test Coverage

Coverage 63.13%

Importance

Changes 0
Metric Value
wmc 48
eloc 124
dl 0
loc 323
ccs 113
cts 179
cp 0.6313
rs 8.5599
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A getCRMCurrencyName() 0 7 1
A getCleanInstance() 0 3 1
A getCurrencyNum() 0 3 1
A getActiveBankName() 0 3 1
A fetchCurrencyRates() 0 22 5
A setActiveBankById() 0 10 2
A getCurrencyRateId() 0 6 1
A updateCurrencyRate() 0 5 1
A getActiveBankId() 0 3 1
A getSupportedCurrencies() 0 16 4
B getUnSupportedCurrencies() 0 23 7
C getCRMConversionRate() 0 34 12
B refreshBanks() 0 25 7
A addCurrencyRate() 0 9 1
A getRatesHistory() 0 14 2
A setCRMConversionRate() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like Settings_CurrencyUpdate_Module_Model 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.

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 Settings_CurrencyUpdate_Module_Model, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @copyright YetiForce S.A.
5
 * @license   YetiForce Public License 5.0 (licenses/LicenseEN.txt or yetiforce.com)
6
 * @author    Mariusz Krzaczkowski <[email protected]>
7
 * @author    Maciej Stencel <[email protected]>
8
 * @author    Radosław Skrzypczak <[email protected]>
9
 */
10
class Settings_CurrencyUpdate_Module_Model extends \App\Base
11
{
12
	// Returns objects instance
13
14 3
	public static function getCleanInstance()
15
	{
16 3
		return new self();
17
	}
18
19
	/**
20
	 * Returns CRMS active currency name by currency code.
21
	 *
22
	 * @param mixed $code
23
	 *
24 2
	 * @return string - currency name
25
	 */
26 2
	public static function getCRMCurrencyName($code)
27 2
	{
28 2
		return (new \App\Db\Query())
0 ignored issues
show
Bug Best Practice introduced by
The expression return new App\Db\Query(...e' => $code))->scalar() could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
29 2
			->select(['currency_name'])
30 2
			->from('vtiger_currencies')
31
			->where(['currency_code' => $code])
32
			->scalar();
33
	}
34
35
	/**
36
	 * Returns list of active currencies in CRM.
37
	 *
38 1
	 * @return <Integer> - number of currencies
0 ignored issues
show
Documentation Bug introduced by
The doc comment <Integer> at position 0 could not be parsed: Unknown type name '<' at position 0 in <Integer>.
Loading history...
39
	 */
40 1
	public function getCurrencyNum()
41
	{
42
		return \count(\App\Fields\Currency::getAll(true));
43
	}
44
45
	/**
46
	 * Returns currency exchange rates for systems active currencies from bank.
47
	 *
48
	 * @param string $dateCur - date for which to fetch exchange rates
49
	 * @param bool   $cron    - true if fired by server, and so updates systems currency conversion rates
50 1
	 *
51
	 * @return bool - true if fetched new exchange rates, false otherwise
52 1
	 */
53
	public function fetchCurrencyRates($dateCur, $cron = false): bool
0 ignored issues
show
Unused Code introduced by
The parameter $cron is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

53
	public function fetchCurrencyRates($dateCur, /** @scrutinizer ignore-unused */ $cron = false): bool

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
54
	{
55 1
		if (!\App\RequestUtil::isNetConnection() || \count(($currencies = \App\Fields\Currency::getAll(true))) <= 1) {
56 1
			return false;
57 1
		}
58 1
		$notifyNewRates = false;
59 1
60 1
		$defaultId = \App\Fields\Currency::getDefault()['id'];
61 1
		unset($currencies[$defaultId]);
62 1
		$currIds = array_column($currencies, 'id', 'currency_code');
63 1
64 1
		$selectBankName = $this->getActiveBankName();
65 1
		$modelClassName = Vtiger_Loader::getComponentClassName('BankModel', $selectBankName, 'Settings:CurrencyUpdate');
66 1
67 1
		if (class_exists($modelClassName) && (new \App\Db\Query())->from('yetiforce_currencyupdate')
68 1
			->where(['exchange_date' => $dateCur, 'currency_id' => array_values($currIds), 'bank_id' => $this->getActiveBankId()])->count(1) !== \count($currIds)) {
69 1
			$bank = new $modelClassName();
70 1
			$bank->getRates($currIds, $dateCur, false);
71
			$notifyNewRates = true;
72 1
		}
73 1
74 1
		return $notifyNewRates;
75 1
	}
76
77 1
	// Synchronises database banks list with the bank classes existing on ftp
78
79
	public function refreshBanks()
80
	{
81
		$db = App\Db::getInstance();
82
		$dataReader = (new \App\Db\Query())->select(['id', 'bank_name'])
83 1
			->from('yetiforce_currencyupdate_banks')
84
			->createCommand()->query();
85
		while ($row = $dataReader->read()) {
86
			$id = $row['id'];
87
			$bankPath = ROOT_DIRECTORY . "/modules/Settings/CurrencyUpdate/bankmodels/{$row['bank_name']}.php";
88 1
			if (!file_exists($bankPath)) { // delete bank from database
89
				$db->createCommand()->delete('yetiforce_currencyupdate_banks', ['id' => $id])->execute();
90 1
			}
91 1
		}
92 1
		$dataReader->close();
93 1
		foreach (new DirectoryIterator(ROOT_DIRECTORY . '/modules/Settings/CurrencyUpdate/bankmodels/') as $fileInfo) {
94 1
			if (!$fileInfo->isFile() || 'php' !== $fileInfo->getExtension()) {
95
				continue;
96
			}
97
			$bankClassName = $fileInfo->getBasename('.php');
98
			$isExists = (new \App\Db\Query())->from('yetiforce_currencyupdate_banks')->where(['bank_name' => $bankClassName])->exists();
99
			if (!$isExists) {
100
				$db->createCommand()->insert('yetiforce_currencyupdate_banks', ['bank_name' => $bankClassName, 'active' => 0])->execute();
101 1
			}
102 1
		}
103 1
		\App\Cache::delete('ActiveBankForExchangeRate', '');
104 1
	}
105
106 1
	/**
107 1
	 * Update currency rate in archives.
108
	 *
109
	 * @param <Integer> $id       - exchange rate id
0 ignored issues
show
Documentation Bug introduced by
The doc comment <Integer> at position 0 could not be parsed: Unknown type name '<' at position 0 in <Integer>.
Loading history...
110 1
	 * @param <Float>   $exchange - exchange rate
111 1
	 */
112 1
	public function updateCurrencyRate($id, $exchange)
113
	{
114
		\App\Db::getInstance()->createCommand()
115 1
			->update('yetiforce_currencyupdate', ['exchange' => $exchange], ['id' => $id])
116 1
			->execute();
117
	}
118 1
119
	/**
120
	 * Adds currency exchange rate to archive.
121
	 *
122
	 * @param <Integer> $currId       - currency id
0 ignored issues
show
Documentation Bug introduced by
The doc comment <Integer> at position 0 could not be parsed: Unknown type name '<' at position 0 in <Integer>.
Loading history...
123
	 * @param <Date>    $exchangeDate - exchange date
124
	 * @param <Float>   $exchange     - exchange rate
125
	 * @param <Integer> $bankId       - bank id
126 1
	 */
127
	public function addCurrencyRate($currId, $exchangeDate, $exchange, $bankId)
128 1
	{
129 1
		\App\Db::getInstance()->createCommand()->insert('yetiforce_currencyupdate', [
130 1
			'currency_id' => $currId,
131 1
			'fetch_date' => date('Y-m-d'),
132
			'exchange_date' => $exchangeDate,
133
			'exchange' => $exchange,
134
			'bank_id' => $bankId,
135
		])->execute();
136
	}
137
138
	/**
139
	 * Returns currency exchange rate id.
140
	 *
141 1
	 * @param <Integer> $currencyId   - systems currency id
0 ignored issues
show
Documentation Bug introduced by
The doc comment <Integer> at position 0 could not be parsed: Unknown type name '<' at position 0 in <Integer>.
Loading history...
142
	 * @param <Date>    $exchangeDate - date of exchange rate
143 1
	 * @param <Integer> $bankId       - id of bank
144 1
	 *
145 1
	 * @return <Integer> - currency rate id
0 ignored issues
show
Documentation Bug introduced by
The doc comment <Integer> at position 0 could not be parsed: Unknown type name '<' at position 0 in <Integer>.
Loading history...
146 1
	 */
147 1
	public function getCurrencyRateId($currencyId, $exchangeDate, $bankId)
148 1
	{
149 1
		return (new \App\Db\Query())->select(['id'])
150 1
			->from('yetiforce_currencyupdate')
151
			->where(['exchange_date' => $exchangeDate, 'currency_id' => $currencyId, 'bank_id' => $bankId])
152
			->scalar();
153
	}
154
155
	/**
156
	 * Returns currency rates from archive.
157
	 *
158
	 * @param int    $bankId    - bank id
159
	 * @param string $dateStart - date
160 1
	 * @param string $dateEnd   - date
161
	 *
162 1
	 * @return <Array> - array containing currency rates
0 ignored issues
show
Documentation Bug introduced by
The doc comment <Array> at position 0 could not be parsed: Unknown type name '<' at position 0 in <Array>.
Loading history...
163 1
	 */
164 1
	public function getRatesHistory(int $bankId, string $dateStart, string $dateEnd)
165 1
	{
166
		$query = (new App\Db\Query())->select(['exchange', 'currency_name', 'currency_code', 'currency_symbol', 'fetch_date', 'exchange_date', 'currency_id'])
167
			->from('yetiforce_currencyupdate')
168
			->innerJoin('vtiger_currency_info', 'vtiger_currency_info.id = yetiforce_currencyupdate.currency_id')
169
			->innerJoin('yetiforce_currencyupdate_banks', 'yetiforce_currencyupdate_banks.id = yetiforce_currencyupdate.bank_id')
170
			->where(['yetiforce_currencyupdate.bank_id' => $bankId]);
171
		// filter by date - if not exists then display this months history
172
		if ($dateEnd) {
173
			$query->andWhere(['between', 'exchange_date', $dateStart, $dateEnd]);
174
		} else {
175
			$query->andWhere(['exchange_date' => $dateStart]);
176
		}
177
		return $query->orderBy(['exchange_date' => SORT_DESC, 'currency_id' => SORT_ASC])->all();
178
	}
179
180
	/**
181
	 * Returns list of supported currencies by active bank.
182
	 *
183
	 * @param string $bankName - bank name
184
	 *
185
	 * @return <Array> - array of supported currencies
0 ignored issues
show
Documentation Bug introduced by
The doc comment <Array> at position 0 could not be parsed: Unknown type name '<' at position 0 in <Array>.
Loading history...
186
	 */
187
	public function getSupportedCurrencies($bankName = null)
188
	{
189
		if (!\App\RequestUtil::isNetConnection()) {
190
			return [];
191
		}
192
		if (!$bankName) {
193
			$bankName = 'Settings_CurrencyUpdate_' . $this->getActiveBankName() . '_BankModel';
194
		}
195
		$currencies = [];
196
		try {
197 1
			$bank = new $bankName();
198
			$currencies = $bank->getSupportedCurrencies();
199 1
		} catch (\Throwable $ex) {
200
			\App\Log::error('Error during downloading table: ' . PHP_EOL . $ex->__toString() . PHP_EOL, 'CurrencyUpdate');
201
		}
202 1
		return $currencies;
203 1
	}
204
205 1
	/**
206
	 * Returns list of unsupported currencies by active bank.
207 1
	 *
208
	 * @param string $bankName - bank name
209
	 *
210
	 * @return <Array> - array of unsupported currencies
0 ignored issues
show
Documentation Bug introduced by
The doc comment <Array> at position 0 could not be parsed: Unknown type name '<' at position 0 in <Array>.
Loading history...
211
	 */
212
	public function getUnSupportedCurrencies($bankName = null)
213
	{
214
		if (!\App\RequestUtil::isNetConnection()) {
215
			return [];
216 1
		}
217
		if (!$bankName && !empty($this->getActiveBankName())) {
218 1
			$bankName = 'Settings_CurrencyUpdate_' . $this->getActiveBankName() . '_BankModel';
219
		}
220
		if (!empty($this->getActiveBankName())) {
221 1
			$bank = new $bankName();
222 1
			$supported = $bank->getSupportedCurrencies($bankName);
223
		}
224 1
		$dataReader = (new \App\Db\Query())->select(['currency_name', 'currency_code'])
225 1
			->from('vtiger_currency_info')
226 1
			->where(['currency_status' => 'Active', 'deleted' => 0])
227 1
			->createCommand()->query();
228 1
		while ($row = $dataReader->read()) {
229 1
			$name = $row['currency_name'];
230 1
			$code = $row['currency_code'];
231 1
			$unsupported[$name] = $code;
232 1
		}
233 1
		$dataReader->close();
234
		return array_diff($unsupported, !empty($supported) ? $supported : []);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $unsupported does not seem to be defined for all execution paths leading up to this point.
Loading history...
235 1
	}
236
237 1
	/**
238
	 * Sets systems exchange rate for chosen currency.
239
	 *
240
	 * @param string  $currency - currency code
241
	 * @param <Float> $exchange - exchange rate
0 ignored issues
show
Documentation Bug introduced by
The doc comment <Float> at position 0 could not be parsed: Unknown type name '<' at position 0 in <Float>.
Loading history...
242
	 */
243
	public function setCRMConversionRate($currency, $exchange)
244
	{
245
		$rate = (float) $exchange;
246 1
		\App\Db::getInstance()->createCommand()
247
			->update('vtiger_currency_info', ['conversion_rate' => $rate], ['currency_code' => $currency])
248 1
			->execute();
249 1
	}
250 1
251 1
	/**
252 1
	 * Function that retrieves conversion rate from and to specified currency.
253
	 *
254
	 * @param int    $from - currency id
255
	 * @param int    $to   - currency id
256
	 * @param string $date - date of the exchange rate
257
	 *
258
	 * @return <Float> - conversion rate
0 ignored issues
show
Documentation Bug introduced by
The doc comment <Float> at position 0 could not be parsed: Unknown type name '<' at position 0 in <Float>.
Loading history...
259
	 */
260
	public function getCRMConversionRate(int $from, int $to, string $date)
261
	{
262 1
		$mainCurrencyCode = \App\Fields\Currency::getDefault()['id'];
263
		$exchange = 0;
264 1
265 1
		if ($to != $mainCurrencyCode && ($activeBankId = self::getActiveBankId())) {
0 ignored issues
show
Bug Best Practice introduced by
The method Settings_CurrencyUpdate_...odel::getActiveBankId() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

265
		if ($to != $mainCurrencyCode && ($activeBankId = self::/** @scrutinizer ignore-call */ getActiveBankId())) {
Loading history...
266 1
			$exchange = (float) (\App\Fields\Currency::getCurrencyRatesFromArchive($date, $to, $activeBankId)['exchange'] ?? 0);
267 1
			if (empty($exchange) && \App\RequestUtil::isNetConnection()) {
268
				self::fetchCurrencyRates($date);
0 ignored issues
show
Bug Best Practice introduced by
The method Settings_CurrencyUpdate_...l::fetchCurrencyRates() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

268
				self::/** @scrutinizer ignore-call */ 
269
          fetchCurrencyRates($date);
Loading history...
269
				$exchange = (float) (\App\Fields\Currency::getCurrencyRatesFromArchive($date, $to, $activeBankId)['exchange'] ?? 0);
270 1
			}
271
		}
272
273
		if ($exchange) {
274 1
			$exchange = 1 / $exchange;
275 1
		}
276 1
277 1
		if ($from != $mainCurrencyCode && ($activeBankId = self::getActiveBankId())) {
278 1
			$convertToMainCurrency = 0 == $exchange ? 1 : 1 / $exchange;
279 1
280 1
			$fromExchange = (float) (\App\Fields\Currency::getCurrencyRatesFromArchive($date, $from, $activeBankId)['exchange'] ?? 0);
281 1
			if (empty($fromExchange) && \App\RequestUtil::isNetConnection()) {
282
				self::fetchCurrencyRates($date);
283
				$fromExchange = (float) (\App\Fields\Currency::getCurrencyRatesFromArchive($date, $from, $activeBankId)['exchange'] ?? 0);
284
			}
285
286
			if ($to != $mainCurrencyCode) {
287
				$exchange = $fromExchange / $convertToMainCurrency;
288
			} else {
289
				$exchange = $fromExchange * $convertToMainCurrency;
290
			}
291
		}
292
293
		return $exchange = round($exchange, 5);
0 ignored issues
show
Unused Code introduced by
The assignment to $exchange is dead and can be removed.
Loading history...
294
	}
295
296
	/**
297
	 * Returns id of active bank.
298
	 *
299
	 * @return <Integer> - bank id
0 ignored issues
show
Documentation Bug introduced by
The doc comment <Integer> at position 0 could not be parsed: Unknown type name '<' at position 0 in <Integer>.
Loading history...
300
	 */
301
	public function getActiveBankId()
302
	{
303
		return \App\Fields\Currency::getActiveBankForExchangeRateUpdate()['id'] ?? 0;
304
	}
305
306
	/**
307
	 * Saves new active bank by id.
308
	 *
309
	 * @param <Integer> $bankId - bank id
0 ignored issues
show
Documentation Bug introduced by
The doc comment <Integer> at position 0 could not be parsed: Unknown type name '<' at position 0 in <Integer>.
Loading history...
310
	 *
311
	 * @return bool - true on success or false
312
	 */
313
	public function setActiveBankById($bankId)
314
	{
315
		$db = \App\Db::getInstance();
316
		$result = $db->createCommand()->update('yetiforce_currencyupdate_banks', ['active' => 0])->execute();
317
		if (!empty($bankId)) {
318
			$result = $db->createCommand()->update('yetiforce_currencyupdate_banks', ['active' => 1], ['id' => $bankId])->execute();
319
		}
320
		\App\Cache::delete('ActiveBankForExchangeRate', '');
321
322
		return (bool) $result;
323
	}
324
325
	/**
326
	 * Returns active banks name.
327
	 *
328
	 * @return string - bank name
329
	 */
330
	public function getActiveBankName()
331
	{
332
		return \App\Fields\Currency::getActiveBankForExchangeRateUpdate()['bank_name'] ?? '';
333
	}
334
}
335