1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
26:
27: 28: 29: 30: 31:
32: class Mage_Tax_Model_Calculation extends Mage_Core_Model_Abstract
33: {
34: const CALC_TAX_BEFORE_DISCOUNT_ON_EXCL = '0_0';
35: const CALC_TAX_BEFORE_DISCOUNT_ON_INCL = '0_1';
36: const CALC_TAX_AFTER_DISCOUNT_ON_EXCL = '1_0';
37: const CALC_TAX_AFTER_DISCOUNT_ON_INCL = '1_1';
38:
39: const CALC_UNIT_BASE = 'UNIT_BASE_CALCULATION';
40: const CALC_ROW_BASE = 'ROW_BASE_CALCULATION';
41: const CALC_TOTAL_BASE = 'TOTAL_BASE_CALCULATION';
42:
43: protected $_rates = array();
44: protected $_ctc = array();
45: protected $_ptc = array();
46:
47: protected $_rateCache = array();
48: protected $_rateCalculationProcess = array();
49:
50: protected $_customer = null;
51: protected $_defaultCustomerTaxClass = null;
52:
53: protected function _construct()
54: {
55: $this->_init('tax/calculation');
56: }
57:
58: 59: 60: 61: 62: 63:
64: public function setCustomer(Mage_Customer_Model_Customer $customer)
65: {
66: $this->_customer = $customer;
67: return $this;
68: }
69:
70: public function getDefaultCustomerTaxClass($store = null)
71: {
72: if ($this->_defaultCustomerTaxClass === null) {
73: $defaultCustomerGroup = Mage::helper('customer')->getDefaultCustomerGroupId($store);
74: $this->_defaultCustomerTaxClass = Mage::getModel('customer/group')->getTaxClassId($defaultCustomerGroup);
75: }
76: return $this->_defaultCustomerTaxClass;
77: }
78:
79: 80: 81: 82: 83:
84: public function getCustomer()
85: {
86: if ($this->_customer === null) {
87: $session = Mage::getSingleton('customer/session');
88: if ($session->isLoggedIn()) {
89: $this->_customer = $session->getCustomer();
90: } elseif ($session->getCustomerId()) {
91: $this->_customer = Mage::getModel('customer/customer')->load($session->getCustomerId());
92: } else {
93: $this->_customer = false;
94: }
95: }
96: return $this->_customer;
97: }
98:
99: 100: 101: 102: 103: 104:
105: public function deleteByRuleId($ruleId)
106: {
107: $this->_getResource()->deleteByRuleId($ruleId);
108: return $this;
109: }
110:
111: 112: 113: 114: 115: 116:
117: public function getRates($ruleId)
118: {
119: if (!isset($this->_rates[$ruleId])) {
120: $this->_rates[$ruleId] = $this->_getResource()->getDistinct('tax_calculation_rate_id', $ruleId);
121: }
122: return $this->_rates[$ruleId];
123: }
124:
125: 126: 127: 128: 129: 130:
131: public function getCustomerTaxClasses($ruleId)
132: {
133: if (!isset($this->_ctc[$ruleId])) {
134: $this->_ctc[$ruleId] = $this->_getResource()->getDistinct('customer_tax_class_id', $ruleId);
135: }
136: return $this->_ctc[$ruleId];
137: }
138:
139: 140: 141: 142: 143: 144:
145: public function getProductTaxClasses($ruleId)
146: {
147: if (!isset($this->_ptc[$ruleId])) {
148: $this->_ptc[$ruleId] = $this->getResource()->getDistinct('product_tax_class_id', $ruleId);
149: }
150: return $this->_ptc[$ruleId];
151: }
152:
153: 154: 155: 156: 157:
158: protected function _formCalculationProcess()
159: {
160: $title = $this->getRateTitle();
161: $value = $this->getRateValue();
162: $id = $this->getRateId();
163:
164: $rate = array('code'=>$title, 'title'=>$title, 'percent'=>$value, 'position'=>1, 'priority'=>1);
165:
166: $process = array();
167: $process['percent'] = $value;
168: $process['id'] = "{$id}-{$value}";
169: $process['rates'][] = $rate;
170:
171: return array($process);
172: }
173:
174: 175: 176: 177: 178: 179:
180: public function getRate($request)
181: {
182: if (!$request->getCountryId() || !$request->getCustomerClassId() || !$request->getProductClassId()) {
183: return 0;
184: }
185:
186: $cacheKey = $this->_getRequestCacheKey($request);
187: if (!isset($this->_rateCache[$cacheKey])) {
188: $this->unsRateValue();
189: $this->unsCalculationProcess();
190: $this->unsEventModuleId();
191: Mage::dispatchEvent('tax_rate_data_fetch', array('request'=>$request));
192: if (!$this->hasRateValue()) {
193: $rateInfo = $this->_getResource()->getRateInfo($request);
194: $this->setCalculationProcess($rateInfo['process']);
195: $this->setRateValue($rateInfo['value']);
196: } else {
197: $this->setCalculationProcess($this->_formCalculationProcess());
198: }
199: $this->_rateCache[$cacheKey] = $this->getRateValue();
200: $this->_rateCalculationProcess[$cacheKey] = $this->getCalculationProcess();
201: }
202: return $this->_rateCache[$cacheKey];
203: }
204:
205: 206: 207: 208: 209: 210:
211: protected function _getRequestCacheKey($request)
212: {
213: $key = $request->getStore() ? $request->getStore()->getId() . '|' : '';
214: $key.= $request->getProductClassId() . '|' . $request->getCustomerClassId() . '|'
215: . $request->getCountryId() . '|'. $request->getRegionId() . '|' . $request->getPostcode();
216: return $key;
217: }
218:
219: 220: 221: 222: 223: 224: 225: 226:
227: public function getStoreRate($request, $store=null)
228: {
229: $storeRequest = $this->getRateOriginRequest($store)
230: ->setProductClassId($request->getProductClassId());
231: return $this->getRate($storeRequest);
232: }
233:
234: 235: 236: 237: 238: 239:
240: public function getRateOriginRequest($store = null)
241: {
242: $request = new Varien_Object();
243: $request->setCountryId(Mage::getStoreConfig(Mage_Shipping_Model_Config::XML_PATH_ORIGIN_COUNTRY_ID, $store))
244: ->setRegionId(Mage::getStoreConfig(Mage_Shipping_Model_Config::XML_PATH_ORIGIN_REGION_ID, $store))
245: ->setPostcode(Mage::getStoreConfig(Mage_Shipping_Model_Config::XML_PATH_ORIGIN_POSTCODE, $store))
246: ->setCustomerClassId($this->getDefaultCustomerTaxClass($store))
247: ->setStore($store);
248: return $request;
249: }
250:
251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265:
266: public function getRateRequest(
267: $shippingAddress = null,
268: $billingAddress = null,
269: $customerTaxClass = null,
270: $store = null)
271: {
272: if ($shippingAddress === false && $billingAddress === false && $customerTaxClass === false) {
273: return $this->getRateOriginRequest($store);
274: }
275: $address = new Varien_Object();
276: $customer = $this->getCustomer();
277: $basedOn = Mage::getStoreConfig(Mage_Tax_Model_Config::CONFIG_XML_PATH_BASED_ON, $store);
278:
279: if (($shippingAddress === false && $basedOn == 'shipping')
280: || ($billingAddress === false && $basedOn == 'billing')) {
281: $basedOn = 'default';
282: } else {
283: if ((($billingAddress === false || is_null($billingAddress) || !$billingAddress->getCountryId())
284: && $basedOn == 'billing')
285: || (($shippingAddress === false || is_null($shippingAddress) || !$shippingAddress->getCountryId())
286: && $basedOn == 'shipping')
287: ){
288: if ($customer) {
289: $defBilling = $customer->getDefaultBillingAddress();
290: $defShipping = $customer->getDefaultShippingAddress();
291:
292: if ($basedOn == 'billing' && $defBilling && $defBilling->getCountryId()) {
293: $billingAddress = $defBilling;
294: } else if ($basedOn == 'shipping' && $defShipping && $defShipping->getCountryId()) {
295: $shippingAddress = $defShipping;
296: } else {
297: $basedOn = 'default';
298: }
299: } else {
300: $basedOn = 'default';
301: }
302: }
303: }
304:
305: switch ($basedOn) {
306: case 'billing':
307: $address = $billingAddress;
308: break;
309: case 'shipping':
310: $address = $shippingAddress;
311: break;
312: case 'origin':
313: $address = $this->getRateOriginRequest($store);
314: break;
315: case 'default':
316: $address
317: ->setCountryId(Mage::getStoreConfig(
318: Mage_Tax_Model_Config::CONFIG_XML_PATH_DEFAULT_COUNTRY,
319: $store))
320: ->setRegionId(Mage::getStoreConfig(Mage_Tax_Model_Config::CONFIG_XML_PATH_DEFAULT_REGION, $store))
321: ->setPostcode(Mage::getStoreConfig(
322: Mage_Tax_Model_Config::CONFIG_XML_PATH_DEFAULT_POSTCODE,
323: $store));
324: break;
325: }
326:
327: if (is_null($customerTaxClass) && $customer) {
328: $customerTaxClass = $customer->getTaxClassId();
329: } elseif (($customerTaxClass === false) || !$customer) {
330: $customerTaxClass = $this->getDefaultCustomerTaxClass($store);
331: }
332:
333: $request = new Varien_Object();
334: $request
335: ->setCountryId($address->getCountryId())
336: ->setRegionId($address->getRegionId())
337: ->setPostcode($address->getPostcode())
338: ->setStore($store)
339: ->setCustomerClassId($customerTaxClass);
340: return $request;
341: }
342:
343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354:
355: public function compareRequests($first, $second)
356: {
357: $country = $first->getCountryId() == $second->getCountryId();
358:
359: $region = (int)$first->getRegionId() == (int)$second->getRegionId();
360: $postcode= $first->getPostcode() == $second->getPostcode();
361: $taxClass= $first->getCustomerClassId() == $second->getCustomerClassId();
362:
363: if ($country && $region && $postcode && $taxClass) {
364: return true;
365: }
366: 367: 368:
369: $firstReqRates = $this->_getResource()->getRateIds($first);
370: $secondReqRates = $this->_getResource()->getRateIds($second);
371: if ($firstReqRates === $secondReqRates) {
372: return true;
373: }
374:
375: 376: 377: 378:
379: $productClassId1 = $first->getProductClassId();
380: $productClassId2 = $second->getProductClassId();
381:
382:
383: $ids = is_array($productClassId1) ? $productClassId1 : array($productClassId1);
384: $identical = true;
385: foreach ($ids as $productClassId) {
386: $first->setProductClassId($productClassId);
387: $rate1 = $this->getRate($first);
388:
389: $second->setProductClassId($productClassId);
390: $rate2 = $this->getRate($second);
391:
392: if ($rate1 != $rate2) {
393: $identical = false;
394: break;
395: }
396: }
397:
398: $first->setProductClassId($productClassId1);
399: $second->setProductClassId($productClassId2);
400:
401: return $identical;
402: }
403:
404: protected function _getRates($request, $fieldName, $type)
405: {
406: $result = array();
407: $classes = Mage::getModel('tax/class')->getCollection()
408: ->addFieldToFilter('class_type', $type)
409: ->load();
410: foreach ($classes as $class) {
411: $request->setData($fieldName, $class->getId());
412: $result[$class->getId()] = $this->getRate($request);
413: }
414:
415: return $result;
416: }
417:
418: public function getRatesForAllProductTaxClasses($request)
419: {
420: return $this->_getRates($request, 'product_class_id', Mage_Tax_Model_Class::TAX_CLASS_TYPE_PRODUCT);
421: }
422: public function getRatesForAllCustomerTaxClasses($request)
423: {
424: return $this->_getRates($request, 'customer_class_id', Mage_Tax_Model_Class::TAX_CLASS_TYPE_CUSTOMER);
425: }
426:
427: 428: 429: 430: 431: 432:
433: public function getAppliedRates($request)
434: {
435: $cacheKey = $this->_getRequestCacheKey($request);
436: if (!isset($this->_rateCalculationProcess[$cacheKey])) {
437: $this->_rateCalculationProcess[$cacheKey] = $this->_getResource()->getCalculationProcess($request);
438: }
439: return $this->_rateCalculationProcess[$cacheKey];
440: }
441:
442: public function reproduceProcess($rates)
443: {
444: return $this->getResource()->getCalculationProcess(null, $rates);
445: }
446:
447: public function getRatesByCustomerTaxClass($customerTaxClass)
448: {
449: return $this->getResource()->getRatesByCustomerTaxClass($customerTaxClass);
450: }
451:
452: public function getRatesByCustomerAndProductTaxClasses($customerTaxClass, $productTaxClass)
453: {
454: return $this->getResource()->getRatesByCustomerTaxClass($customerTaxClass, $productTaxClass);
455: }
456:
457: 458: 459: 460: 461: 462: 463: 464: 465:
466: public function calcTaxAmount($price, $taxRate, $priceIncludeTax=false, $round=true)
467: {
468: $taxRate = $taxRate/100;
469:
470: if ($priceIncludeTax) {
471: $amount = $price*(1-1/(1+$taxRate));
472: } else {
473: $amount = $price*$taxRate;
474: }
475:
476: if ($round) {
477: return $this->round($amount);
478: }
479:
480: return $amount;
481: }
482:
483: 484: 485: 486: 487: 488: 489:
490: public function truncate($price, $precision=4)
491: {
492: $exp = pow(10,$precision);
493: $price = floor($price*$exp)/$exp;
494: return $price;
495: }
496:
497: 498: 499: 500: 501: 502:
503: public function round($price)
504: {
505: return Mage::app()->getStore()->roundPrice($price);
506: }
507:
508: 509: 510: 511: 512: 513:
514: public function roundUp($price)
515: {
516: return ceil($price*100)/100;
517: }
518: }
519: