1: <?php
2: /*
3:
4: *
5: * NOTICE OF LICENSE
6: *
7: * This source file is subject to the Open Software License (OSL 3.0)
8: * or OpenGPL v3 license (GNU Public License V3.0)
9: * that is bundled with this package in the file LICENSE.txt.
10: * It is also available through the world-wide-web at this URL:
11: * http://opensource.org/licenses/osl-3.0.php
12: * or
13: * http://www.gnu.org/licenses/gpl-3.0.txt
14: * If you did not receive a copy of the license and are unable to
15: * obtain it through the world-wide-web, please send an email
16: * to info@e-abi.ee so we can send you a copy immediately.
17: *
18: * DISCLAIMER
19: *
20: * Do not edit or add to this file if you wish to upgrade this module to newer
21: * versions in the future.
22: *
23: * @category Eabi
24: * @package Eabi_Dpd
25: * @copyright Copyright (c) 2014 Aktsiamaailm LLC (http://en.e-abi.ee/)
26: * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
27: * @license http://www.gnu.org/licenses/gpl-3.0.txt GNU Public License V3.0
28: * @author Matis Halmann
29: *
30:
31: */
32:
33: /**
34: * Helper class for dealing with situation, when Carrier provider needs some shipment data to be sent to Carrier providers server.
35: *
36: * @author Aktsiamaailm OÜ, Matis Halmann
37: */
38: class Eabi_Postoffice_Helper_Data extends Mage_Core_Helper_Abstract {
39: private static $timeDiff = null;
40:
41: /**
42: * <p>Attempts to send order shipment data right at the moment, when user lands on the success page
43: * and user has just completed onepage checkout.</p>
44: * <p>Shipment data is sent to the server only, when automatic data sending is enabled and the timing is set to right after order completion.</p>
45: * <p>Shipment data is sent to the server in the following conditions:
46: * <ul>
47: * <li>Order has been fully paid, or payment method is Cash on delivery (to be implemented in future) and cash on delivery is allowed</li>
48: * <li>Carrier supports automatic shipment data sending to the Carrier server</li>
49: * <li>Merchant has enabled automatic shipment data sending to the server</li>
50: * <li>Shipment data has not been sent to the server earlier</li>
51: * </ul>
52: * </p>
53: * @see Eabi_Postoffice_Model_Source_Sendevent source for possible automatic data sending moments.
54: *
55: */
56: public function autoSendAfterOnepage() {
57: $incrementId = Mage::getSingleton('checkout/session')->getLastRealOrderId();
58: if ($incrementId != '') {
59: $this->handleOrder($incrementId, 'after_checkout');
60: }
61:
62: }
63:
64: /**
65: * <p>Attempts to send order shipment data right at the moment, when user lands on the success page
66: * and user has just completed multishipping checkout.</p>
67: * <p>Shipment data is sent to the server only, when automatic data sending is enabled and the timing is set to right after order completion.</p>
68: * <p>Shipment data is sent to the server in the following conditions:
69: * <ul>
70: * <li>Order has been fully paid, or payment method is Cash on delivery (to be implemented in future) and cash on delivery is allowed</li>
71: * <li>Carrier supports automatic shipment data sending to the Carrier server</li>
72: * <li>Merchant has enabled automatic shipment data sending to the server</li>
73: * <li>Shipment data has not been sent to the server earlier</li>
74: * </ul>
75: * </p>
76: * @see Eabi_Postoffice_Model_Source_Sendevent source for possible automatic data sending moments.
77: *
78: */
79: public function autoSendAfterMultishipping() {
80: $ids = Mage::getSingleton('core/session')->getOrderIds(true);
81: if ($ids && is_array($ids)) {
82: foreach ($ids as $item) {
83: $this->handleOrder($item, 'after_checkout');
84: }
85: }
86:
87: }
88:
89: /**
90: * <p>Attempts to send order shipment data right at the moment, when Merchant has finished "Create Shipping" procedure for the order.
91: * </p>
92: * <p>Shipment data is sent to the server only, when automatic data sending is enabled and the timing is set to right after order completion.</p>
93: * <p>Shipment data is sent to the server in the following conditions:
94: * <ul>
95: * <li>Order has been fully paid, or payment method is Cash on delivery (to be implemented in future) and cash on delivery is allowed</li>
96: * <li>Carrier supports automatic shipment data sending to the Carrier server</li>
97: * <li>Merchant has enabled automatic shipment data sending to the server</li>
98: * <li>Shipment data has not been sent to the server earlier</li>
99: * </ul>
100: * </p>
101: * @see Eabi_Postoffice_Model_Source_Sendevent source for possible automatic data sending moments.
102: *
103: */
104: public function autoSendAfterShipment($observer) {
105: $event = $observer->getEvent();
106: $shipment = $event->getShipment();
107: $order = $shipment->getOrder();
108: $incrementId = $order->getIncrementId();
109: if ($shipment->getId() <= 0) {
110: $this->handleOrder($incrementId, 'after_shipment');
111: }
112: }
113:
114: /**
115: * <p>Clears all the extra data created by this carrier or by the subclasses of this carrier.</p>
116: *
117: * <p>It is called with the event <code>sales_order_place_before</code></p>
118: *
119: *
120: * @param Varien_Object $observer
121: * @throws Exception
122: */
123: public function clearSessions($observer) {
124: $carriers = Mage::getModel('eabi_postoffice/carriermodule')->getCollection();
125:
126: foreach ($carriers as $carrier) {
127: $shippingModel = Mage::getModel($carrier->getData('class_name'));
128: if (!($shippingModel instanceof Eabi_Postoffice_Model_Carrier_Abstract)) {
129: //when disabling modules, which are included on the eabi_carriermodule table
130: //it may cause them not to be the instance of Eabi_Postoffice_Model_Carrier_Abstract any more
131: //to combat this issue, we simply need to ignore the loaded shipping model from the table.
132: //this way we do not cause Magento's own order creation process to fail because of previously disabled modules
133: continue;
134: // throw new Exception('Invalid Shipping model');
135: }
136: $shippingModel->clearSession();
137: }
138: }
139:
140: /**
141: * <p>Attempts to send order shipment data right from the Merchant's manual call.
142: * </p>
143: * <p>Shipment data is sent to the server in the following conditions:
144: * <ul>
145: * <li>Order has been fully paid, or payment method is Cash on delivery (to be implemented in future) and cash on delivery is allowed</li>
146: * <li>Carrier supports automatic shipment data sending to the Carrier server</li>
147: * <li>Merchant has enabled automatic shipment data sending to the server</li>
148: * <li>Shipment data has not been sent to the server earlier</li>
149: * </ul>
150: * </p>
151: * @see Eabi_Postoffice_Model_Source_Sendevent source for possible automatic data sending moments.
152: *
153: * @param string $incrementId Order increment ID
154: * @param string $eventName Name of the event.
155: *
156: */
157: public function sendManualOrderData($incrementId, $eventName) {
158: return $this->handleOrder($incrementId, $eventName);
159: }
160:
161: /**
162: *
163: * <p>Attempts to fetch the barcode, which has been generated by the remote Carrier.</p>
164: * <p>Fetching barcode is only possible, when shipment data is successfully sent to the carrier
165: * and Carrier itself supports Barcodes.</p>
166: * <p>Barcodes are used to print shipping labels.</p>
167: *
168: * @param string $incrementId Order increment ID.
169: * @return string|boolean barcode for the selected order, when successul, false in every other situation.
170: */
171: public function getBarcode($incrementId) {
172: $order = Mage::getModel('sales/order')->loadByIncrementId($incrementId);
173: if (!is_object($order) || $order->getId() <= 0) {
174: return false;
175: }
176: $shippingMethod = $order->getShippingMethod();
177: $shippingCarrierCode = substr($shippingMethod, 0, strpos($shippingMethod, '_'));
178: $shippingMethodModel = Mage::getModel('shipping/shipping')->getCarrierByCode($shippingCarrierCode, $order->getStoreId());
179: if ($shippingMethodModel && ($shippingMethodModel instanceof Eabi_Postoffice_Model_Carrier_Abstract)) {
180: return $shippingMethodModel->getBarcode($order);
181: }
182: return false;
183:
184: }
185:
186: /**
187: * <p>Attempts to fetch shipping method instance for specified order increment id.</p>
188: * <p>Shipping method is fetched only if it is instance of <code>Eabi_Postoffice_Model_Carrier_Abstract</code> otherwise boolean false is returned</p>
189: * @param string $incrementId order increment id
190: * @return Eabi_Postoffice_Model_Carrier_Abstract|boolean
191: */
192: public function getShippingMethodInstance($incrementId) {
193: $order = Mage::getModel('sales/order')->loadByIncrementId($incrementId);
194: if (!is_object($order) || $order->getId() <= 0) {
195: return false;
196: }
197: $shippingMethod = $order->getShippingMethod();
198: $shippingCarrierCode = substr($shippingMethod, 0, strpos($shippingMethod, '_'));
199: $shippingMethodModel = Mage::getModel('shipping/shipping')->getCarrierByCode($shippingCarrierCode, $order->getStoreId());
200: if ($shippingMethodModel && ($shippingMethodModel instanceof Eabi_Postoffice_Model_Carrier_Abstract)) {
201: $shippingMethodModel->setStore($order->getStore());
202: return $shippingMethodModel;
203: }
204: return false;
205: }
206:
207:
208: /**
209: *
210: * <p>Attempts to fetch the shipping label PDF created by the remote Carrier server.</p>
211: * <p>Fetching barcode is only possible, when shipment data is successfully sent to the carrier
212: * and Carrier itself supports Barcodes.</p>
213: *
214: * @param string $incrementId Order increment ID.
215: * @return string|boolean Packing slip PDF binary for the selected order when successful, false in every other condition.
216: */
217: public function getBarcodePdf($incrementId) {
218: $order = Mage::getModel('sales/order')->loadByIncrementId($incrementId);
219: if (!is_object($order) || $order->getId() <= 0) {
220: return false;
221: }
222: $shippingMethod = $order->getShippingMethod();
223: $shippingCarrierCode = substr($shippingMethod, 0, strpos($shippingMethod, '_'));
224: $shippingMethodModel = Mage::getModel('shipping/shipping')->getCarrierByCode($shippingCarrierCode, $order->getStoreId());
225: if ($shippingMethodModel && ($shippingMethodModel instanceof Eabi_Postoffice_Model_Carrier_Abstract)) {
226: return $shippingMethodModel->getBarcodePdf($order);
227: }
228: return false;
229:
230: }
231:
232: /**
233: * <p>Determines whether Order shipment data has been successfully sent to the remote carrier server.</p>
234: *
235: * @param string $incrementId Order increment ID.
236: * @return boolean true, if the data has been successfully sent to the server. False, if the data has not been sent
237: * successfully to the server. NULL if automatic data sending is not enabled or not applicable for the Order selected carrier.
238: */
239: public function isDataSent($incrementId) {
240: $order = Mage::getModel('sales/order')->loadByIncrementId($incrementId);
241: if (!is_object($order) || $order->getId() <= 0) {
242: return null;
243: }
244: $shippingMethod = $order->getShippingMethod();
245: $shippingCarrierCode = substr($shippingMethod, 0, strpos($shippingMethod, '_'));
246: $shippingMethodModel = Mage::getModel('shipping/shipping')->getCarrierByCode($shippingCarrierCode, $order->getStoreId());
247: if ($shippingMethodModel && ($shippingMethodModel instanceof Eabi_Postoffice_Model_Carrier_Abstract)) {
248: if ($shippingMethodModel->isAutoSendAvailable()) {
249: return $shippingMethodModel->isDataSent($order);
250: }
251: }
252: return null;
253:
254: }
255:
256: /**
257: * <p>Returns true if all of the following are true</p>
258: * <ul>
259: <li>Carrier supports automatic data sending</li>
260: <li>Automatic data sending is allowed</li>
261: <li>Order is fully paid or payment method is COD</li>
262: <li>Order is not canceled and contains physical goods</li>
263: </ul>
264: * @param Mage_Sales_Model_Order $order
265: * @param type $shippingMethodModel
266: * @return boolean
267: */
268: public function canSendData(Mage_Sales_Model_Order $order, &$shippingMethodModel = null) {
269: if (!$shippingMethodModel) {
270: $shippingMethod = $order->getShippingMethod();
271: $paymentMethod = $order->getPayment();
272:
273: //get the shipping code from the order and call the module from it.
274: $shippingCarrierCode = substr($shippingMethod, 0, strpos($shippingMethod, '_'));
275: $shippingMethodModel = Mage::getModel('shipping/shipping')->getCarrierByCode($shippingCarrierCode, $order->getStoreId());
276: }
277:
278:
279: $codMethods = array(
280: 'eabicodpayment',
281: 'dpdcodpayment',
282: );
283:
284: if ($shippingMethodModel && ($shippingMethodModel instanceof Eabi_Postoffice_Model_Carrier_Abstract)
285: && $shippingMethodModel->getConfigData('senddata_enable')
286: && (!(round($order->getTotalDue(), 2) > 0)
287:
288: || ((round($order->getTotalDue(), 2) > 0 && in_array($paymentMethod->getMethod(), $codMethods))))
289: && !($order->isCanceled() || $order->getIsVirtual())) {
290: $shippingMethodModel->setStore($order->getStore());
291: return true;
292: }
293: return false;
294: }
295:
296: /**
297: *
298: * <p>Attempts to auto send data to remote server and insert the result to order comments.</p>
299: * <p>Shipment data is sent to the server in the following conditions:
300: * <ul>
301: * <li>Order has been fully paid, or payment method is Cash on delivery</li>
302: * <li>Carrier supports automatic shipment data sending to the Carrier server</li>
303: * <li>Merchant has enabled automatic shipment data sending to the server</li>
304: * <li>Shipment data has not been sent to the server earlier</li>
305: * <li>$eventName matches data sending event set up in the configuration.</li>
306: * </ul>
307: * </p>
308: * @param string $incrementId order increment id
309: * @param string $eventName event name for send data
310: * @return null
311: * @see Eabi_Postoffice_Model_Source_Sendevent
312: */
313: private function handleOrder($incrementId, $eventName) {
314: $order = Mage::getModel('sales/order')->loadByIncrementId($incrementId);
315: if (!is_object($order) || $order->getId() <= 0) {
316: return;
317: }
318: $shippingMethod = $order->getShippingMethod();
319: $paymentMethod = $order->getPayment();
320:
321: //get the shipping code from the order and call the module from it.
322: $shippingCarrierCode = substr($shippingMethod, 0, strpos($shippingMethod, '_'));
323: $shippingMethodModel = Mage::getModel('shipping/shipping')->getCarrierByCode($shippingCarrierCode, $order->getStoreId());
324: $codMethods = array(
325: 'eabicodpayment',
326: 'dpdcodpayment',
327: );
328:
329:
330: if ($shippingMethodModel && ($shippingMethodModel instanceof Eabi_Postoffice_Model_Carrier_Abstract)
331: && $shippingMethodModel->getConfigData('senddata_enable')
332: && $shippingMethodModel->getConfigData('senddata_event') == $eventName
333: /* order due is 0*/
334: && (!(round($order->getTotalDue(), 2) > 0)
335: /* order due is larger than 0 and payment method is in allowed list */
336: || ((round($order->getTotalDue(), 2) > 0 && in_array($paymentMethod->getMethod(), $codMethods)))) && !($order->isCanceled() || $order->getIsVirtual()) && $shippingMethodModel->isDataSent($order) === false) {
337: try {
338: $resultCarrierId = substr($shippingMethod, strrpos($shippingMethod, '_') + 1);
339: //TODO - make sure the correct store is selected, for example in admin.
340: $request = new Varien_Object();
341: Mage::dispatchEvent('eabi_postoffice_autosend_data_before', array(
342: 'request' => $request,
343: 'shipment_method' => $shippingMethodModel,
344: 'order' => $order,
345: ));
346:
347:
348: $result = $shippingMethodModel->autoSendData($order, $resultCarrierId);
349: if ($result) {
350: $order->addStatusHistoryComment(print_r($result, true));
351:
352: $request = new Varien_Object();
353: Mage::dispatchEvent('eabi_postoffice_autosend_data_success', array(
354: 'request' => $request,
355: 'shipment_method' => $shippingMethodModel,
356: 'order' => $order,
357: 'result' => $result,
358: ));
359: } else {
360: $order->addStatusHistoryComment($this->__('Automatic data sending not applicable'));
361: }
362: $order->save();
363: } catch (Exception $e) {
364: $order->addStatusHistoryComment($e->__toString());
365: $order->save();
366: }
367: }
368: }
369:
370:
371: /**
372: * <p>Attempts to fetch data from order that is stored by carriers extending Eabi_Postoffice_Model_Carrier_Abstract</p>
373: * <p>Data is stored in not visible on front order comment base64 encoded form and starting with specified prefix.</p>
374: * <p>If matching comment is found, it is decoded and returned as assoc array</p>
375: * @param Mage_Sales_Model_Order $order Magento order instance to look up the data for
376: * @param string $prefix unique string prefix order comment should start with.
377: * @return array
378: */
379: public function getDataFromOrder(Mage_Sales_Model_Order $order, $prefix) {
380: $orderData = array();
381: foreach ($order->getAllStatusHistory() as $statusHistory) {
382: /* @var $statusHistory Mage_Sales_Model_Order_Status_History */
383: if ($statusHistory->getComment() && !$statusHistory->getVisibleOnFront()) {
384: if ($this->_commentContainsValidData($statusHistory->getComment(), $prefix)) {
385: $orderData = @json_decode(@gzuncompress(@base64_decode($this->_getFromComment($statusHistory->getComment(), $prefix))), true);
386: if (!is_array($orderData)) {
387: //unserialize error on recognized pattern, should throw error or at least log
388: $orderData = array();
389: }
390: }
391: }
392:
393:
394: }
395: return $orderData;
396:
397: }
398:
399: /**
400: * <p>Stores extra data for specified order in single order comment, which will start with specified prefix.</p>
401: * <p>If no matching order comment is found, then it is created automatically, otherwise old one is updated.</p>
402: * <p>Order comment is stored using following procedure:</p>
403: * <ul>
404: <li>If old data is found, then it is merged with new data</li>
405: <li>Data is json encoded and after that gzcompressed</li>
406: <li>Now it is base64 encoded and divided into 40 char long lines and prefixed with $prefix</li>
407: <li>Result is stored to one of the comments contained within the order.</li>
408: </ul>
409: * @param Mage_Sales_Model_Order $order Magento order instance to set up the data for
410: * @param array $data
411: * @param string $prefix
412: * @return array
413: */
414: public function setDataToOrder(Mage_Sales_Model_Order $order, array $data, $prefix) {
415: $oldOrderData = $this->getDataFromOrder($order, $prefix);
416: if (isset($data['comment_id'])) {
417: unset($data['comment_id']);
418: }
419: if (count($oldOrderData) && isset($oldOrderData['comment_id'])) {
420: //we have old data
421: $history = Mage::getModel('sales/order_status_history')->load($oldOrderData['comment_id']);
422: if ($history && $history->getId()) {
423: foreach ($data as $k => $v) {
424: $oldOrderData[$k] = $v;
425: }
426: $history->setComment($this->_getCommentFromData($oldOrderData, $prefix));
427: $history->save();
428: }
429: //comment id for example.....
430: } else {
431: //we do not have old data, so add new comment
432: //set the id also
433:
434: $history = Mage::getModel('sales/order_status_history')
435: ->setStatus($order->getStatus())
436: ->setOrderId($order->getId())
437: ->setParentId($order->getId())
438: ->setComment($this->_getCommentFromData($data, $prefix))
439: ->save();
440: $commentId = $history->getId();
441:
442: $data['comment_id'] = $commentId;
443: $history->setComment($this->_getCommentFromData($data, $prefix));
444: $history->save();
445: }
446:
447:
448:
449: return $history;
450: }
451:
452:
453:
454: /**
455: * <p>Returns Magento timestamp and time() difference in seconds</p>
456: * @return int
457: */
458: public function getTimeDiff() {
459: if (self::$timeDiff === null) {
460: $time = time();
461: $now = Mage::getModel('core/date')->timestamp($time);
462: echo '<pre>'.htmlspecialchars(print_r($time, true)).'</pre>';
463: echo '<pre>'.htmlspecialchars(print_r($now, true)).'</pre>';
464: self::$timeDiff = $time - $now;
465: }
466: return self::$timeDiff;
467:
468: }
469:
470:
471: protected function _getCommentFromData($data, $prefix) {
472: return $prefix ."\n". chunk_split(base64_encode(gzcompress(json_encode($data))), 40, "\n");
473: }
474:
475: protected function _getFromComment($comment, $prefix) {
476: return str_replace($prefix, '', str_replace("\n", '', $comment));
477: }
478:
479:
480:
481: protected function _commentContainsValidData($comment, $prefix) {
482: //TODO: refactor to something better
483: return strpos($comment, $prefix) === 0
484: && strlen($comment) > strlen($prefix);
485: }
486:
487: /**
488: * <p>Takes in array of parcel weights and returns number of packages calculated by maximum allowed weight per package</p>
489: * <p>Uses better methology to find number of packages than regular cart weight divided by maximum package weight</p>
490: * <p>For example, if maximum package weight is 31kg, ang we have 3x 20kg packages, then number of packages would be 3 (not 2)</p>
491: * <p>If maximum package weight is not defined, then it returns 1</p>
492: * <p>If single item in <code>$itemWeights</code> exceeds <code>$maximumWeight</code> then this function returns false</p>
493: * @param array $itemWeights array of item weights
494: * @param int $maximumWeight maximum allowed weight of one package
495: * @return int
496: */
497: public function getNumberOfPackagesFromItemWeights(array $itemWeights, $maximumWeight) {
498: $numPackages = 1;
499: $weight = 0;
500: if ($maximumWeight > 0) {
501:
502: foreach ($itemWeights as $itemWeight) {
503: if ($itemWeight > $maximumWeight) {
504: return false;
505: }
506: $weight += $itemWeight;
507: if ($weight > $maximumWeight) {
508: $numPackages++;
509: $weight = $itemWeight;
510: }
511: }
512:
513: }
514: return $numPackages;
515: }
516:
517: /**
518: *
519: * @param string $number
520: * @param Mage_Customer_Model_Address $address
521: * @return boolean
522: */
523: public function isMobilePhone(&$number, $address) {
524: $countryId = $address->getCountryId();
525: $number = (string)$number;
526:
527: $customerNumbers = $this->_getCountryCodeHelper()->separatePhoneNumberFromCountryCode($number, $countryId);
528:
529: $numberToTest = $customerNumbers['dial_code']. $customerNumbers['phone_number'];
530:
531: switch ($countryId) {
532: case 'EE':
533: if (strlen($numberToTest) >= 10 && substr($numberToTest, 0, 5) === '+3725') {
534: return true;
535: }
536: break;
537: case 'LV':
538: if (strlen($numberToTest) >= 12 && substr($numberToTest, 0, 5) === '+3712') {
539: return true;
540: }
541: break;
542: case 'LT':
543: if (strlen($numberToTest) >= 11 && substr($numberToTest, 0, 5) === '+3706') {
544: return true;
545: }
546: if (strlen($numberToTest) >= 12 && substr($numberToTest, 0, 6) === '+37086') {
547: // $number = substr($number, 1);
548: return true;
549: }
550: break;
551: default:
552: break;
553: }
554: return false;
555: }
556:
557:
558: public function setCarrierLicense($carrierCode, array $data) {
559: $carrierModule = $this->_getCarrierModule($carrierCode);
560: if ($carrierModule->getNeedsLicense()) {
561: $license = base64_encode(gzcompress(json_encode($data)));
562: $carrierModule->setLicenseData($license);
563: $carrierModule->save();
564: return true;
565: }
566: return false;
567: }
568:
569: public function getCarrierLicense($carrierCode) {
570: $carrierModule = $this->_getCarrierModule($carrierCode);
571: if ($carrierModule->getNeedsLicense() && $carrierModule->getLicenseData()) {
572: $license = @json_decode(@gzuncompress(@base64_decode($carrierModule->getLicenseData())), true);
573: if (!is_array($license)) {
574: return false;
575: }
576: return $license;
577: }
578: return false;
579: }
580:
581:
582: /**
583: *
584: * @return Eabi_Postoffice_Model_Carriermodule
585: */
586: protected function _getCarrierModule($carrierCode) {
587: $carrierModules = $this->_getCarrierModuleModel()
588: ->getCollection()
589: ->addFieldToFilter('carrier_code', $carrierCode);
590: return $carrierModules->getFirstItem();
591: }
592:
593: /**
594: *
595: * @return Eabi_Postoffice_Model_Carriermodule
596: */
597: protected function _getCarrierModuleModel() {
598: return Mage::getModel('eabi_postoffice/carriermodule');
599: }
600:
601:
602:
603: /**
604: *
605: * @return Eabi_Postoffice_Helper_Countrycode
606: */
607: protected function _getCountryCodeHelper() {
608: return Mage::helper('eabi_postoffice/countrycode');
609: }
610:
611:
612: }
613: