Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Calculation.php
Go to the documentation of this file.
1 <?php
11 
13 {
17  const USA_COUNTRY_CODE = 'US';
18 
24  protected $_ratesCache = [];
25 
31  protected $_taxData;
32 
36  protected $_storeManager;
37 
44  public function __construct(
45  \Magento\Framework\Model\ResourceModel\Db\Context $context,
46  \Magento\Tax\Helper\Data $taxData,
48  $connectionName = null
49  ) {
50  $this->_taxData = $taxData;
51  $this->_storeManager = $storeManager;
52  parent::__construct($context, $connectionName);
53  }
54 
60  protected function _construct()
61  {
62  $this->_setMainTable('tax_calculation');
63  }
64 
71  public function deleteByRuleId($ruleId)
72  {
73  $conn = $this->getConnection();
74  $where = $conn->quoteInto('tax_calculation_rule_id = ?', (int)$ruleId);
75  $conn->delete($this->getMainTable(), $where);
76 
77  return $this;
78  }
79 
87  public function getCalculationsById($field, $ruleId)
88  {
89  $select = $this->getConnection()->select();
90  $select->from($this->getMainTable(), $field)->where('tax_calculation_rule_id = ?', (int)$ruleId);
91 
92  return $this->getConnection()->fetchCol($select);
93  }
94 
101  public function getRateInfo($request)
102  {
103  $rates = $this->_getRates($request);
104  return [
105  'process' => $this->getCalculationProcess($request, $rates),
106  'value' => $this->_calculateRate($rates)
107  ];
108  }
109 
116  public function getRate($request)
117  {
118  return $this->_calculateRate($this->_getRates($request));
119  }
120 
130  public function getCalculationProcess($request, $rates = null)
131  {
132  if ($rates === null) {
133  $rates = $this->_getRates($request);
134  }
135 
136  $result = [];
137  $row = [];
138  $ids = [];
139  $currentRate = 0;
140  $totalPercent = 0;
141  $countedRates = count($rates);
142  for ($i = 0; $i < $countedRates; $i++) {
143  $rate = $rates[$i];
144  $value = (isset($rate['value']) ? $rate['value'] : $rate['percent']) * 1;
145 
146  $oneRate = [
147  'code' => $rate['code'],
148  'title' => $rate['title'],
149  'percent' => $value,
150  'position' => $rate['position'],
151  'priority' => $rate['priority']
152  ];
153  if (isset($rate['tax_calculation_rule_id'])) {
154  $oneRate['rule_id'] = $rate['tax_calculation_rule_id'];
155  }
156 
157  if (isset($rate['hidden'])) {
158  $row['hidden'] = $rate['hidden'];
159  }
160 
161  if (isset($rate['amount'])) {
162  $row['amount'] = $rate['amount'];
163  }
164 
165  if (isset($rate['base_amount'])) {
166  $row['base_amount'] = $rate['base_amount'];
167  }
168  if (isset($rate['base_real_amount'])) {
169  $row['base_real_amount'] = $rate['base_real_amount'];
170  }
171  $row['rates'][] = $oneRate;
172 
173  $ruleId = null;
174  if (isset($rates[$i + 1]['tax_calculation_rule_id'])) {
175  $ruleId = $rate['tax_calculation_rule_id'];
176  }
177  $priority = $rate['priority'];
178  $ids[] = $rate['code'];
179 
180  if (isset($rates[$i + 1]['tax_calculation_rule_id'])) {
181  while (isset($rates[$i + 1]) && $rates[$i + 1]['tax_calculation_rule_id'] == $ruleId) {
182  $i++;
183  }
184  }
185 
186  $currentRate += $value;
187 
188  if (!isset(
189  $rates[$i + 1]
190  ) || $rates[$i + 1]['priority'] != $priority || isset(
191  $rates[$i + 1]['process']
192  ) && $rates[$i + 1]['process'] != $rate['process']
193  ) {
194  if (!empty($rates[$i]['calculate_subtotal'])) {
195  $row['percent'] = $currentRate;
196  $totalPercent += $currentRate;
197  } else {
198  $row['percent'] = $this->_collectPercent($totalPercent, $currentRate);
199  $totalPercent += $row['percent'];
200  }
201  $row['id'] = implode('', $ids);
202  $result[] = $row;
203  $row = [];
204  $ids = [];
205 
206  $currentRate = 0;
207  }
208  }
209 
210  return $result;
211  }
212 
220  protected function _collectPercent($percent, $rate)
221  {
222  return (100 + $percent) * ($rate / 100);
223  }
224 
232  protected function _createSearchPostCodeTemplates($postcode, $exactPostcode = null)
233  {
234  // as needed, reduce the postcode to the correct length
235  $len = $this->_taxData->getPostCodeSubStringLength();
236  $postcode = substr($postcode, 0, $len);
237 
238  // begin creating the search template array
239  $strArr = [$postcode, $postcode . '*'];
240 
241  // if supplied, use the exact postcode as the basis for the search templates
242  if ($exactPostcode) {
243  $postcode = substr($exactPostcode, 0, $len);
244  $strArr[] = $postcode;
245  }
246 
247  // finish building out the search template array
248  $strlen = strlen($postcode);
249  for ($i = 1; $i < $strlen; $i++) {
250  $strArr[] = sprintf('%s*', substr($postcode, 0, -$i));
251  }
252 
253  return $strArr;
254  }
255 
266  protected function _getRates($request)
267  {
268  // Extract params that influence our SELECT statement and use them to create cache key
269  $storeId = $this->_storeManager->getStore($request->getStore())->getId();
270  $customerClassId = $request->getCustomerClassId();
271  $countryId = $request->getCountryId();
272  $regionId = $request->getRegionId();
273  $postcode = $request->getPostcode();
274 
275  // Process productClassId as it can be array or usual value. Form best key for cache.
276  $productClassId = $request->getProductClassId();
277  $ids = is_array($productClassId) ? $productClassId : [$productClassId];
278  foreach ($ids as $key => $val) {
279  $ids[$key] = (int)$val; // Make it integer for equal cache keys even in case of null/false/0 values
280  }
281  $ids = array_unique($ids);
282  sort($ids);
283  $productClassKey = implode(',', $ids);
284 
285  // Form cache key and either get data from cache or from DB
286  $cacheKey = implode(
287  '|',
288  [$storeId, $customerClassId, $productClassKey, $countryId, $regionId, $postcode]
289  );
290 
291  if (!isset($this->_ratesCache[$cacheKey])) {
292  // Make SELECT and get data
293  $select = $this->getConnection()->select();
294  $select->from(
295  ['main_table' => $this->getMainTable()],
296  [
297  'tax_calculation_rate_id',
298  'tax_calculation_rule_id',
299  'customer_tax_class_id',
300  'product_tax_class_id'
301  ]
302  )->where(
303  'customer_tax_class_id = ?',
304  (int)$customerClassId
305  );
306  if ($productClassId) {
307  $select->where('product_tax_class_id IN (?)', $productClassId);
308  }
309  $ifnullTitleValue = $this->getConnection()->getCheckSql(
310  'title_table.value IS NULL',
311  'rate.code',
312  'title_table.value'
313  );
314  $ruleTableAliasName = $this->getConnection()->quoteIdentifier('rule.tax_calculation_rule_id');
315  $select->join(
316  ['rule' => $this->getTable('tax_calculation_rule')],
317  $ruleTableAliasName . ' = main_table.tax_calculation_rule_id',
318  ['rule.priority', 'rule.position', 'rule.calculate_subtotal']
319  )->join(
320  ['rate' => $this->getTable('tax_calculation_rate')],
321  'rate.tax_calculation_rate_id = main_table.tax_calculation_rate_id',
322  [
323  'value' => 'rate.rate',
324  'rate.tax_country_id',
325  'rate.tax_region_id',
326  'rate.tax_postcode',
327  'rate.tax_calculation_rate_id',
328  'rate.code'
329  ]
330  )->joinLeft(
331  ['title_table' => $this->getTable('tax_calculation_rate_title')],
332  "rate.tax_calculation_rate_id = title_table.tax_calculation_rate_id " .
333  "AND title_table.store_id = '{$storeId}'",
334  ['title' => $ifnullTitleValue]
335  )->where(
336  'rate.tax_country_id = ?',
337  $countryId
338  )->where(
339  "rate.tax_region_id IN(?)",
340  [0, (int)$regionId]
341  );
342  $postcodeIsNumeric = is_numeric($postcode);
343  $postcodeIsRange = false;
344  $originalPostcode = null;
345  if (is_string($postcode) && preg_match('/^(.+)-(.+)$/', $postcode, $matches)) {
346  if ($countryId == self::USA_COUNTRY_CODE && is_numeric($matches[2]) && strlen($matches[2]) == 4) {
347  $postcodeIsNumeric = true;
348  $originalPostcode = $postcode;
349  $postcode = $matches[1];
350  } else {
351  $postcodeIsRange = true;
352  $zipFrom = $matches[1];
353  $zipTo = $matches[2];
354  }
355  }
356 
357  if ($postcodeIsNumeric || $postcodeIsRange) {
358  $selectClone = clone $select;
359  $selectClone->where('rate.zip_is_range IS NOT NULL');
360  }
361  $select->where('rate.zip_is_range IS NULL');
362 
363  if ($postcode != '*' || $postcodeIsRange) {
364  $select->where(
365  "rate.tax_postcode IS NULL OR rate.tax_postcode IN('*', '', ?)",
366  $postcodeIsRange ? $postcode : $this->_createSearchPostCodeTemplates($postcode, $originalPostcode)
367  );
368  if ($postcodeIsNumeric) {
369  $selectClone->where('? BETWEEN rate.zip_from AND rate.zip_to', $postcode);
370  } elseif ($postcodeIsRange) {
371  $selectClone->where('rate.zip_from >= ?', $zipFrom)
372  ->where('rate.zip_to <= ?', $zipTo);
373  }
374  }
375 
379  if ($postcodeIsNumeric || $postcodeIsRange) {
380  $select = $this->getConnection()->select()->union(
381  ['(' . $select . ')', '(' . $selectClone . ')']
382  );
383  }
384 
385  $select->order(
386  'priority ' . \Magento\Framework\DB\Select::SQL_ASC
387  )->order(
388  'tax_calculation_rule_id ' . \Magento\Framework\DB\Select::SQL_ASC
389  )->order(
390  'tax_country_id ' . \Magento\Framework\DB\Select::SQL_DESC
391  )->order(
392  'tax_region_id ' . \Magento\Framework\DB\Select::SQL_DESC
393  )->order(
394  'tax_postcode ' . \Magento\Framework\DB\Select::SQL_DESC
395  )->order(
396  'value ' . \Magento\Framework\DB\Select::SQL_DESC
397  );
398 
399  $fetchResult = $this->getConnection()->fetchAll($select);
400  $filteredRates = [];
401  if ($fetchResult) {
402  foreach ($fetchResult as $rate) {
403  if (!isset($filteredRates[$rate['tax_calculation_rate_id']])) {
404  $filteredRates[$rate['tax_calculation_rate_id']] = $rate;
405  }
406  }
407  }
408  $this->_ratesCache[$cacheKey] = array_values($filteredRates);
409  }
410 
411  return $this->_ratesCache[$cacheKey];
412  }
413 
420  protected function _calculateRate($rates)
421  {
422  $result = 0;
423  $currentRate = 0;
424  $countedRates = count($rates);
425  for ($i = 0; $i < $countedRates; $i++) {
426  $rate = $rates[$i];
427  $rule = $rate['tax_calculation_rule_id'];
428  $value = $rate['value'];
429  $priority = $rate['priority'];
430 
431  while (isset($rates[$i + 1]) && $rates[$i + 1]['tax_calculation_rule_id'] == $rule) {
432  $i++;
433  }
434 
435  $currentRate += $value;
436 
437  if (!isset($rates[$i + 1]) || $rates[$i + 1]['priority'] != $priority) {
438  if (!empty($rates[$i]['calculate_subtotal'])) {
439  $result += $currentRate;
440  } else {
441  $result += $this->_collectPercent($result, $currentRate);
442  }
443  $currentRate = 0;
444  }
445  }
446 
447  return $result;
448  }
449 
456  public function getRateIds($request)
457  {
458  $result = [];
459  $rates = $this->_getRates($request);
460  $countedRates = count($rates);
461  for ($i = 0; $i < $countedRates; $i++) {
462  $rate = $rates[$i];
463  $rule = $rate['tax_calculation_rule_id'];
464  $result[] = $rate['tax_calculation_rate_id'];
465  while (isset($rates[$i + 1]) && $rates[$i + 1]['tax_calculation_rule_id'] == $rule) {
466  $i++;
467  }
468  }
469 
470  return $result;
471  }
472 }
if($this->helper('Magento\Tax\Helper\Data') ->displayFullSummary()) foreach( $block->getTotal() ->getFullInfo() as $info)(isset($info['hidden']) && $info['hidden']) $percent
Definition: tax.phtml:33
_createSearchPostCodeTemplates($postcode, $exactPostcode=null)
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
$storeManager
getCalculationProcess($request, $rates=null)
$rates
Definition: tax.phtml:35
$value
Definition: gender.phtml:16
__construct(\Magento\Framework\Model\ResourceModel\Db\Context $context, \Magento\Tax\Helper\Data $taxData, \Magento\Store\Model\StoreManagerInterface $storeManager, $connectionName=null)
Definition: Calculation.php:44
$i
Definition: gallery.phtml:31
_setMainTable($mainTable, $idFieldName=null)
Definition: AbstractDb.php:230