/system/app/Tools/Tax/Tax.php

https://github.com/seosamba/ecommerce · PHP · 263 lines · 187 code · 29 blank · 47 comment · 65 complexity · 614391192c1b4dff4e56be5e0c83adcb MD5 · raw file

  1. <?php
  2. /**
  3. * Handy tools for work with taxes
  4. *
  5. * @author Eugene I. Nezhuta <eugene@seotoaster.com>
  6. */
  7. class Tools_Tax_Tax {
  8. const ZONE_TYPE_ZIP = 'zip';
  9. const ZONE_TYPE_STATE = 'state';
  10. const ZONE_TYPE_COUNTRY = 'country';
  11. /**
  12. * Calculates product tax price according to configured rules
  13. * @param Models_Model_Product $product Product model
  14. * @param null $destinationAddress If not specified uses default tax rule for calculation
  15. * @param bool $taxRateOnly If true returns only appropriate tax rate
  16. * @return float|int
  17. */
  18. public static function calculateProductTax(Models_Model_Product $product, $destinationAddress = null, $taxRateOnly = false) {
  19. $isTaxable = self::isTaxableGroup();
  20. if(($taxClass = $product->getTaxClass()) != 0 && $isTaxable === true) {
  21. $rateMethodName = 'getRate' . $taxClass;
  22. if (null !== $destinationAddress){
  23. $zoneId = self::getZone($destinationAddress);
  24. if ($zoneId) {
  25. $tax = Models_Mapper_Tax::getInstance()->findByZoneId($zoneId);
  26. }
  27. } else {
  28. $tax = Models_Mapper_Tax::getInstance()->getDefaultRule();
  29. }
  30. if (isset($tax) && $tax !== null) {
  31. $productPrice = is_null($product->getCurrentPrice()) ? $product->getPrice() : $product->getCurrentPrice();
  32. $data = $taxRateOnly ? $tax->$rateMethodName() : ($productPrice / 100) * $tax->$rateMethodName();
  33. return $data;
  34. }
  35. }
  36. return 0;
  37. }
  38. /**
  39. * Calculates shipping tax according to configured rules
  40. * @param float $shippingPrice Shipping price
  41. * @param null $destinationAddress If not specified uses default tax rule for calculation
  42. * @return float|int
  43. */
  44. public static function calculateShippingTax($shippingPrice, $destinationAddress = null) {
  45. $isTaxable = self::isTaxableGroup();
  46. $shippingTaxClass = Models_Mapper_ShoppingConfig::getInstance()->getConfigParam('shippingTaxRate');
  47. if($shippingTaxClass != '0' && $shippingTaxClass != null && $isTaxable === true) {
  48. $getRate = 'getRate'.$shippingTaxClass;
  49. if (null !== $destinationAddress){
  50. $zoneId = self::getZone($destinationAddress);
  51. if ($zoneId) {
  52. $tax = Models_Mapper_Tax::getInstance()->findByZoneId($zoneId);
  53. }
  54. } else {
  55. $tax = Models_Mapper_Tax::getInstance()->getDefaultRule();
  56. }
  57. if (isset($tax) && $tax !== null) {
  58. return ($shippingPrice / 100) * $tax->$getRate();
  59. }
  60. }
  61. return 0;
  62. }
  63. /**
  64. * Calculates discount tax according to configured rules
  65. * @param float $discountPrice Discount price
  66. * @param null $destinationAddress If not specified uses default tax rule for calculation
  67. * @return float|int
  68. */
  69. public static function calculateDiscountTax($discountPrice, $discountTaxRate, $destinationAddress = null) {
  70. $isTaxable = self::isTaxableGroup();
  71. if($discountTaxRate != '0' && $isTaxable === true) {
  72. $getRate = 'getRate'.$discountTaxRate;
  73. if (null !== $destinationAddress){
  74. $zoneId = self::getZone($destinationAddress);
  75. if ($zoneId) {
  76. $tax = Models_Mapper_Tax::getInstance()->findByZoneId($zoneId);
  77. }
  78. } else {
  79. $tax = Models_Mapper_Tax::getInstance()->getDefaultRule();
  80. }
  81. if (isset($tax) && $tax !== null && $getRate !== 'getRate') {
  82. return ($discountPrice / 100) * $tax->$getRate();
  83. }
  84. }
  85. return 0;
  86. }
  87. /**
  88. * Tries to find zone id using all zone types (zip, state, country)
  89. *
  90. * @return int
  91. */
  92. public static function getZone($address = null, $withTaxable = true, $customTaxZoneIds = array()) {
  93. if (is_null($address) || empty($address)){
  94. return 0;
  95. } else {
  96. $address = Tools_Misc::clenupAddress($address);
  97. }
  98. $zones = array();
  99. $zoneMapper = Models_Mapper_Zone::getInstance();
  100. if($withTaxable){
  101. $taxableZones = Models_Mapper_Tax::getInstance()->fetchAll();
  102. if(is_array($taxableZones) && !empty($taxableZones)) {
  103. foreach($taxableZones as $taxZone){
  104. $zoneIds[] = $taxZone->getZoneId();
  105. }
  106. $zones = $zoneMapper->fetchAll($zoneMapper->getDbTable()->getAdapter()->quoteInto('id IN(?)', $zoneIds));
  107. }
  108. }else{
  109. if (!empty($customTaxZoneIds)) {
  110. $zones = $zoneMapper->fetchAll($zoneMapper->getDbTable()->getAdapter()->quoteInto('id IN(?)', $customTaxZoneIds));
  111. } else {
  112. $zones = $zoneMapper->fetchAll();
  113. }
  114. }
  115. if(is_array($zones) && !empty($zones)) {
  116. $zoneMatch = 0;
  117. $maxRate = 0;
  118. foreach($zones as $zone) {
  119. $matchRate = 0;
  120. if (empty($address['country']) && empty($address['state']) && empty($address['zip'])){
  121. continue;
  122. }
  123. $countries = $zone->getCountries(true);
  124. if ($zone->getZip() && !empty($address['zip'])) {
  125. //wildcard zip analyze
  126. $zipMatched = false;
  127. $wildcardZones = preg_grep('~\*~', $zone->getZip());
  128. if (!empty($wildcardZones)) {
  129. foreach ($wildcardZones as $wildcardZone) {
  130. $wildcardPosition = strpos($wildcardZone, '*');
  131. $currentZip = substr_replace($address['zip'], '', $wildcardPosition);
  132. $matchZip = substr_replace($wildcardZone, '', $wildcardPosition);
  133. if ($currentZip === $matchZip && in_array($address['country'], $countries)) {
  134. $matchRate += 5;
  135. $zipMatched = true;
  136. }
  137. }
  138. }
  139. if (in_array($address['zip'], $zone->getZip()) && in_array($address['country'], $countries)
  140. && !$zipMatched) {
  141. $matchRate += 5;
  142. } elseif(!$zipMatched) {
  143. continue;
  144. }
  145. }
  146. if (!empty($address['state'])){
  147. if ($zone->getStates()) {
  148. $states = array_map(function($state){ return $state['id'];}, $zone->getStates());
  149. if (in_array($address['state'], $states)) {
  150. $matchRate += 3;
  151. }
  152. }
  153. //@todo Review this scoring algoryhtm. It looks like we don't need this
  154. // else {
  155. // $matchRate++;
  156. // }
  157. }
  158. if (!empty($countries)) {
  159. if (in_array($address['country'], $countries)){
  160. $matchRate += 1;
  161. }
  162. }
  163. if ($matchRate && $matchRate > $maxRate){
  164. $maxRate = $matchRate;
  165. $zoneMatch = $zone->getId();
  166. }
  167. unset($countries, $states);
  168. }
  169. return $zoneMatch;
  170. }
  171. return 0;
  172. }
  173. /**
  174. * Gives zone id by type such as: zip, state, country
  175. *
  176. * @param Models_Model_Zone $zone
  177. * @param string $type
  178. * @return int
  179. */
  180. public static function getZoneIdByType(Models_Model_Zone $zone, $type = self::ZONE_TYPE_ZIP, $address = null) {
  181. // $address = Models_Mapper_ShoppingConfig::getInstance()->getConfigParams();
  182. $zoneParts = array();
  183. switch($type) {
  184. case self::ZONE_TYPE_ZIP:
  185. $zoneParts = $zone->getZip();
  186. break;
  187. case self::ZONE_TYPE_STATE:
  188. $zoneParts = $zone->getStates();
  189. break;
  190. case self::ZONE_TYPE_COUNTRY:
  191. $zoneParts = $zone->getCountries(true);
  192. break;
  193. }
  194. if(is_array($zoneParts) && !empty($zoneParts)) {
  195. if($type == self::ZONE_TYPE_STATE) {
  196. foreach($zoneParts as $zonePart) {
  197. if($zonePart['id'] == $address['state']) {
  198. return $zone->getId();
  199. }
  200. }
  201. }
  202. if(in_array($address[$type], $zoneParts)) {
  203. return $zone->getId();
  204. }
  205. }
  206. return 0;
  207. }
  208. /**
  209. * Check if user in non taxable group
  210. *
  211. * @return bool
  212. */
  213. public static function isTaxableGroup()
  214. {
  215. $isTaxable = true;
  216. $sessionHelper = Zend_Controller_Action_HelperBroker::getStaticHelper('session');
  217. $user = $sessionHelper->getCurrentUser();
  218. $id = $user->getId();
  219. if (!empty($id)) {
  220. $dbTable = new Zend_Db_Table();
  221. $select = $dbTable->getAdapter()->select()
  222. ->from(array('sg' => 'shopping_group'), array('sg.groupName', 'sg.nonTaxable'))
  223. ->join(array('sci' => 'shopping_customer_info'), 'sg.id = sci.group_id', array())
  224. ->where('sci.user_id = ' . $user->getId());
  225. $userInGroup = $dbTable->getAdapter()->fetchRow($select);
  226. if (!empty($userInGroup)) {
  227. if (!empty($userInGroup['nonTaxable'])) {
  228. $isTaxable = false;
  229. }
  230. }
  231. }
  232. return $isTaxable;
  233. }
  234. }