fermopoint - Version 1.0.2

Version Notes

Initial release.

Download this release

Release Info

Developer Fermo!Point
Extension fermopoint
Version 1.0.2
Comparing to
See all releases


Version 1.0.2

Files changed (58) hide show
  1. app/code/community/FermoPoint/StorePickup/Block/Adminhtml/Configuration/Disabled.php +12 -0
  2. app/code/community/FermoPoint/StorePickup/Block/Adminhtml/Configuration/Manual.php +18 -0
  3. app/code/community/FermoPoint/StorePickup/Block/Adminhtml/Remote.php +15 -0
  4. app/code/community/FermoPoint/StorePickup/Block/Adminhtml/Remote/Grid.php +133 -0
  5. app/code/community/FermoPoint/StorePickup/Block/Adminhtml/Remote/Grid/Renderer/Notes.php +41 -0
  6. app/code/community/FermoPoint/StorePickup/Block/Adminhtml/Stats.php +32 -0
  7. app/code/community/FermoPoint/StorePickup/Block/Checkout/Billing/Js.php +11 -0
  8. app/code/community/FermoPoint/StorePickup/Block/Checkout/Billing/Radio.php +11 -0
  9. app/code/community/FermoPoint/StorePickup/Block/Checkout/Onepage/Payment/Methods.php +39 -0
  10. app/code/community/FermoPoint/StorePickup/Block/Checkout/Onepage/Shipping/Method/Available.php +46 -0
  11. app/code/community/FermoPoint/StorePickup/Block/Map.php +55 -0
  12. app/code/community/FermoPoint/StorePickup/Exception.php +5 -0
  13. app/code/community/FermoPoint/StorePickup/Helper/Config.php +80 -0
  14. app/code/community/FermoPoint/StorePickup/Helper/Data.php +117 -0
  15. app/code/community/FermoPoint/StorePickup/Model/Api.php +226 -0
  16. app/code/community/FermoPoint/StorePickup/Model/Api/OrderCollection.php +46 -0
  17. app/code/community/FermoPoint/StorePickup/Model/Api/SearchData.php +64 -0
  18. app/code/community/FermoPoint/StorePickup/Model/Carrier/Storepickup.php +157 -0
  19. app/code/community/FermoPoint/StorePickup/Model/GoogleMaps.php +75 -0
  20. app/code/community/FermoPoint/StorePickup/Model/Observer.php +241 -0
  21. app/code/community/FermoPoint/StorePickup/Model/Order/Point.php +30 -0
  22. app/code/community/FermoPoint/StorePickup/Model/Point.php +183 -0
  23. app/code/community/FermoPoint/StorePickup/Model/Points.php +53 -0
  24. app/code/community/FermoPoint/StorePickup/Model/Resource/Order/Point.php +18 -0
  25. app/code/community/FermoPoint/StorePickup/Model/Resource/Order/Point/Collection.php +10 -0
  26. app/code/community/FermoPoint/StorePickup/Model/Resource/Point.php +20 -0
  27. app/code/community/FermoPoint/StorePickup/Model/Resource/Point/Collection.php +10 -0
  28. app/code/community/FermoPoint/StorePickup/Model/Source/Payment.php +21 -0
  29. app/code/community/FermoPoint/StorePickup/Model/Source/Selectorpayment.php +12 -0
  30. app/code/community/FermoPoint/StorePickup/Model/Source/State.php +63 -0
  31. app/code/community/FermoPoint/StorePickup/controllers/Adminhtml/RemoteController.php +41 -0
  32. app/code/community/FermoPoint/StorePickup/controllers/IndexController.php +40 -0
  33. app/code/community/FermoPoint/StorePickup/etc/adminhtml.xml +35 -0
  34. app/code/community/FermoPoint/StorePickup/etc/config.xml +200 -0
  35. app/code/community/FermoPoint/StorePickup/etc/jstranslator.xml +27 -0
  36. app/code/community/FermoPoint/StorePickup/etc/system.xml +173 -0
  37. app/code/community/FermoPoint/StorePickup/sql/fpstorepickup_setup/mysql4-install-1.0.0.php +49 -0
  38. app/code/community/FermoPoint/StorePickup/sql/fpstorepickup_setup/mysql4-upgrade-1.0.0-1.0.1.php +146 -0
  39. app/code/community/FermoPoint/StorePickup/sql/fpstorepickup_setup/mysql4-upgrade-1.0.1-1.0.2.php +9 -0
  40. app/design/adminhtml/default/default/layout/fpstorepickup.xml +22 -0
  41. app/design/adminhtml/default/default/template/fpstorepickup/stats.phtml +44 -0
  42. app/design/frontend/base/default/layout/fpstorepickup.xml +15 -0
  43. app/design/frontend/base/default/template/fpstorepickup/checkout/onepage/billing/js.phtml +21 -0
  44. app/design/frontend/base/default/template/fpstorepickup/checkout/onepage/billing/radio.phtml +5 -0
  45. app/design/frontend/base/default/template/fpstorepickup/map.phtml +118 -0
  46. app/etc/modules/FermoPoint_StorePickup.xml +9 -0
  47. app/locale/it_IT/FermoPoint_StorePickup.csv +53 -0
  48. js/fermopoint/markerclusterer.js +1316 -0
  49. js/fermopoint/storepickup.js +254 -0
  50. media/fermopoint/cluster1.png +0 -0
  51. media/fermopoint/cluster2.png +0 -0
  52. media/fermopoint/cluster3.png +0 -0
  53. media/fermopoint/cluster4.png +0 -0
  54. media/fermopoint/marker_location.png +0 -0
  55. media/fermopoint/marker_point.png +0 -0
  56. package.xml +18 -0
  57. skin/frontend/base/default/fermopoint/css/storepickup.css +86 -0
  58. skin/frontend/base/default/fermopoint/images/opc-ajax-loader.gif +0 -0
app/code/community/FermoPoint/StorePickup/Block/Adminhtml/Configuration/Disabled.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Block_Adminhtml_Configuration_Disabled extends Mage_Adminhtml_Block_System_Config_Form_Field
4
+ {
5
+
6
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
7
+ {
8
+ $element->setReadonly(true);
9
+ return parent::_getElementHtml($element);
10
+ }
11
+
12
+ }
app/code/community/FermoPoint/StorePickup/Block/Adminhtml/Configuration/Manual.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Block_Adminhtml_Configuration_Manual extends Mage_Adminhtml_Block_System_Config_Form_Field
4
+ {
5
+
6
+ protected function _decorateRowHtml($element, $html)
7
+ {
8
+ return '<tr id="row_' . $element->getHtmlId() . '">' . $html . '</tr>';
9
+ }
10
+
11
+ public function render(Varien_Data_Form_Element_Abstract $element)
12
+ {
13
+ $message = $this->__('For all the information on installing and configuring the module and FermoPoint Merchant Account, you can refer to <a href="%s" target="_blank">this page</a>', 'https://www.fermopoint.it/guide/magento');
14
+ $html = '<td class="label" colspan="4">' . $message . '</td>';
15
+ return $this->_decorateRowHtml($element, $html);
16
+ }
17
+
18
+ }
app/code/community/FermoPoint/StorePickup/Block/Adminhtml/Remote.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Block_Adminhtml_Remote extends Mage_Adminhtml_Block_Widget_Grid_Container
4
+ {
5
+
6
+ public function __construct()
7
+ {
8
+ $this->_controller = 'adminhtml_remote';
9
+ $this->_blockGroup = 'fpstorepickup';
10
+ $this->_headerText = Mage::helper('fpstorepickup')->__('All Orders');
11
+ parent::__construct();
12
+ $this->removeButton('add');
13
+ }
14
+
15
+ }
app/code/community/FermoPoint/StorePickup/Block/Adminhtml/Remote/Grid.php ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Block_Adminhtml_Remote_Grid extends Mage_Adminhtml_Block_Widget_Grid
4
+ {
5
+ protected $_defaultSort = 'order_id';
6
+
7
+ protected $_parentTemplate;
8
+
9
+ public function __construct()
10
+ {
11
+ parent::__construct();
12
+ $this->setId('remoteGrid');
13
+ $this->setUseAjax(true);
14
+ $this->setDefaultSort('order_id');
15
+ $this->setSaveParametersInSession(true);
16
+ }
17
+
18
+ protected function _createCollection()
19
+ {
20
+ return Mage::getModel('fpstorepickup/api_orderCollection');
21
+ }
22
+
23
+ protected function _prepareCollection()
24
+ {
25
+ $collection = $this->_createCollection();
26
+ $this->setCollection($collection);
27
+
28
+ return parent::_prepareCollection();
29
+ }
30
+
31
+ protected function _prepareColumns()
32
+ {
33
+ $this->addColumn('ticket_id', array(
34
+ 'header' => $this->__('Ticket ID'),
35
+ 'index' => 'ticketId',
36
+ 'filter' => false,
37
+ 'sortable' => false,
38
+ ));
39
+
40
+ $this->addColumn('merchant_id', array(
41
+ 'header' => $this->__('Order ID'),
42
+ 'index' => 'merchant_id',
43
+ 'filter' => false,
44
+ 'sortable' => false,
45
+ ));
46
+
47
+ $this->addColumn('merchant_notes', array(
48
+ 'header' => $this->__('Merchant Notes'),
49
+ 'index' => 'merchant_notes',
50
+ 'filter' => false,
51
+ 'sortable' => false,
52
+ ));
53
+
54
+ $this->addColumn('point_id', array(
55
+ 'header' => $this->__('Point ID'),
56
+ 'index' => 'point_id',
57
+ 'filter' => false,
58
+ 'sortable' => false,
59
+ ));
60
+
61
+ $this->addColumn('nickname', array(
62
+ 'header' => $this->__('Nickname'),
63
+ 'index' => 'nickname',
64
+ 'filter' => false,
65
+ 'sortable' => false,
66
+ ));
67
+
68
+ $this->addColumn('email', array(
69
+ 'header' => $this->__('Email'),
70
+ 'index' => 'email',
71
+ 'filter' => false,
72
+ 'sortable' => false,
73
+ ));
74
+
75
+ $this->addColumn('phone_number', array(
76
+ 'header' => $this->__('Phone Number'),
77
+ 'index' => 'phone_number',
78
+ 'filter' => false,
79
+ 'sortable' => false,
80
+ ));
81
+
82
+ $this->addColumn('state', array(
83
+ 'header' => Mage::helper('fpstorepickup')->__('State'),
84
+ 'align' => 'left',
85
+ 'width' => '80px',
86
+ 'index' => 'state',
87
+ 'type' => 'options',
88
+ 'options' => array(),
89
+ 'options' => Mage::getSingleton('fpstorepickup/source_state')->toOptionMap(),
90
+ 'filter' => false,
91
+ 'sortable' => false,
92
+ ));
93
+
94
+ $this->addColumn('notes', array(
95
+ 'header' => Mage::helper('fpstorepickup')->__('Notes'),
96
+ 'index' => 'notes',
97
+ 'renderer' => 'fpstorepickup/adminhtml_remote_grid_renderer_notes',
98
+ 'filter' => false,
99
+ 'sortable' => false,
100
+ ));
101
+
102
+ $this->addColumn('last_update', array(
103
+ 'header' => Mage::helper('fpstorepickup')->__('Last Update'),
104
+ 'index' => 'last_update',
105
+ 'type' => 'datetime',
106
+ 'filter' => false,
107
+ 'sortable' => false,
108
+ ));
109
+
110
+ $this->addExportType('*/*/exportCsv', Mage::helper('fpstorepickup')->__('CSV'));
111
+ $this->addExportType('*/*/exportXml', Mage::helper('fpstorepickup')->__('Excel XML'));
112
+
113
+ return parent::_prepareColumns();
114
+ }
115
+
116
+ protected function _prepareMassaction()
117
+ {
118
+ $this->setMassactionIdField('log_id');
119
+
120
+ return $this;
121
+ }
122
+
123
+ public function getGridUrl()
124
+ {
125
+ return $this->getUrl('*/*/grid', array('_current'=>true));
126
+ }
127
+
128
+ public function getRowUrl($row)
129
+ {
130
+ return 'javascript:void()';
131
+ }
132
+
133
+ }
app/code/community/FermoPoint/StorePickup/Block/Adminhtml/Remote/Grid/Renderer/Notes.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Block_Adminhtml_Remote_Grid_Renderer_Notes extends Mage_Adminhtml_Block_Widget_Grid_Column_Renderer_Abstract
4
+ {
5
+
6
+ public function render(Varien_Object $row)
7
+ {
8
+ if ( ! is_array($row->getNotes()) || ! count($row->getNotes()))
9
+ return '';
10
+ $block = $this->getLayout()->createBlock('adminhtml/widget_grid_column_renderer_datetime');
11
+ $column = new Varien_Object();
12
+ $column->setData(array(
13
+ 'getter' => null,
14
+ 'index' => 'date',
15
+ ));
16
+ $block->setColumn($column);
17
+ $result = '<table>';
18
+ $result .= '<thead>';
19
+ $result .= '<tr class="headings"><th>'.$this->__('Date').'</th><th>'.$this->__('Note').'</th></tr>';
20
+ $result .= '</thead>';
21
+ $result .= '<tbody>';
22
+ foreach ($row->getNotes() as $entry)
23
+ {
24
+ $data = new Varien_Object();
25
+ $data->setData($entry);
26
+ $result .= '<tr><td>'.$block->render($data).'</td><td>'.$this->escapeHtml($entry['note']).'</td></tr>';
27
+ }
28
+ $result .= '</tbody>';
29
+ $result .= '</table>';
30
+ return $result;
31
+ }
32
+
33
+ public function renderExport(Varien_Object $row)
34
+ {
35
+ $result = array();
36
+ foreach ($row->getNotes() as $entry)
37
+ $result[] = $entry['date'] . ': ' . $entry['note'];
38
+ return implode('; ', $result);
39
+ }
40
+
41
+ }
app/code/community/FermoPoint/StorePickup/Block/Adminhtml/Stats.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Block_Adminhtml_Stats extends Mage_Adminhtml_Block_Template {
4
+
5
+ protected $_merchant;
6
+
7
+ protected function _getMerchant()
8
+ {
9
+ if ($this->_merchant === null)
10
+ {
11
+ try {
12
+ $this->_merchant = Mage::getModel('fpstorepickup/api')->getMerchant();
13
+ } catch (FermoPoint_StorePickup_Exception $e) {
14
+ $this->_merchant = false;
15
+ }
16
+ }
17
+ return $this->_merchant;
18
+ }
19
+
20
+ public function getCredits()
21
+ {
22
+ $merchant = $this->_getMerchant();
23
+ return $merchant !== false ? $merchant['credits'] : false;
24
+ }
25
+
26
+ public function getOrders()
27
+ {
28
+ $merchant = $this->_getMerchant();
29
+ return $merchant !== false ? $merchant['orders'] : array();
30
+ }
31
+
32
+ }
app/code/community/FermoPoint/StorePickup/Block/Checkout/Billing/Js.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Block_Checkout_Billing_Js extends Mage_Core_Block_Template {
4
+
5
+ public function __construct()
6
+ {
7
+ parent::__construct();
8
+ $this->setTemplate('fpstorepickup/checkout/onepage/billing/js.phtml');
9
+ }
10
+
11
+ }
app/code/community/FermoPoint/StorePickup/Block/Checkout/Billing/Radio.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Block_Checkout_Billing_Radio extends Mage_Core_Block_Template {
4
+
5
+ public function __construct()
6
+ {
7
+ parent::__construct();
8
+ $this->setTemplate('fpstorepickup/checkout/onepage/billing/radio.phtml');
9
+ }
10
+
11
+ }
app/code/community/FermoPoint/StorePickup/Block/Checkout/Onepage/Payment/Methods.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Block_Checkout_Onepage_Payment_Methods extends Mage_Checkout_Block_Onepage_Payment_Methods
4
+ {
5
+ public function getMethods()
6
+ {
7
+ $methods = $this->getData('methods');
8
+ if (is_null($methods))
9
+ {
10
+ $store = $this->getQuote() ? $this->getQuote()->getStoreId() : null;
11
+ $methods = $this->helper('payment')->getStoreMethods($store, $this->getQuote());
12
+ foreach ($methods as $key => $method) {
13
+ if ($this->_canUseMethod($method)) {
14
+ $this->_assignMethod($method);
15
+ }
16
+ else {
17
+ unset($methods[$key]);
18
+ }
19
+ }
20
+
21
+ if ($this->getQuote()->getShippingAddress()->getShippingMethod() === 'fpstorepickup_fpstorepickup'
22
+ && Mage::helper('fpstorepickup/config')->getAllowSpecificPayments()
23
+ )
24
+ {
25
+ $allowed = array_flip(Mage::helper('fpstorepickup/config')->getSpecificPayments());
26
+ foreach ($methods as $key => $method)
27
+ {
28
+ if ( ! isset($allowed[$method->getCode()]))
29
+ unset($methods[$key]);
30
+ }
31
+
32
+ $methods = array_values($methods);
33
+ }
34
+
35
+ $this->setData('methods', $methods);
36
+ }
37
+ return $methods;
38
+ }
39
+ }
app/code/community/FermoPoint/StorePickup/Block/Checkout/Onepage/Shipping/Method/Available.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Block_Checkout_Onepage_Shipping_Method_Available extends Mage_Checkout_Block_Onepage_Shipping_Method_Available {
4
+
5
+ protected $_storePickupAvailable = false;
6
+
7
+ protected function _filterGroups($groups)
8
+ {
9
+ $useStorePickup = Mage::helper('fpstorepickup')->getUseMethod();
10
+ foreach ($groups as $group => $rates)
11
+ {
12
+ if ($group === 'fpstorepickup' && $useStorePickup)
13
+ $this->_storePickupAvailable = true;
14
+
15
+ if ($group !== 'fpstorepickup' && $useStorePickup
16
+ || $group === 'fpstorepickup' && ! $useStorePickup
17
+ )
18
+ unset($groups[$group]);
19
+ }
20
+
21
+ return $groups;
22
+ }
23
+
24
+ public function getShippingRates()
25
+ {
26
+ if (empty($this->_rates)) {
27
+ $this->getAddress()->collectShippingRates()->save();
28
+
29
+ $groups = $this->getAddress()->getGroupedAllShippingRates();
30
+
31
+ $this->_rates = $this->_filterGroups($groups);
32
+ }
33
+
34
+ return $this->_rates;
35
+ }
36
+
37
+ protected function _afterToHtml($html)
38
+ {
39
+ if ($this->_storePickupAvailable)
40
+ $html .= $this->getLayout()->createBlock('fpstorepickup/map')->toHtml();
41
+
42
+ return $html;
43
+ }
44
+
45
+ }
46
+
app/code/community/FermoPoint/StorePickup/Block/Map.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Block_Map extends Mage_Core_Block_Template
4
+ {
5
+
6
+ public function __construct()
7
+ {
8
+ parent::__construct();
9
+
10
+ $this->setTemplate('fpstorepickup/map.phtml');
11
+ }
12
+
13
+ public function getTosUrl()
14
+ {
15
+ return Mage::helper('fpstorepickup/config')->getTosUrl();
16
+ }
17
+
18
+ public function getGoogleMaps($callback = null)
19
+ {
20
+ $params = array(
21
+ 'v' => '3.exp',
22
+ 'region' => 'it',
23
+ );
24
+ if ($apiKey = Mage::helper('fpstorepickup/config')->getGMapsKey())
25
+ $params['key'] = $apiKey;
26
+ if ($callback)
27
+ $params['callback'] = $callback;
28
+ return 'https://maps.googleapis.com/maps/api/js?' . http_build_query($params);
29
+ }
30
+
31
+ public function getBillingAddress()
32
+ {
33
+ $address = Mage::getSingleton('checkout/session')->getQuote()->getBillingAddress();
34
+ $street = $address->getStreet();
35
+ $parts = array(
36
+ (is_array($street) ? implode(', ', $street) : $street) . ',',
37
+ $address->getPostcode(),
38
+ $address->getCity(),
39
+ );
40
+
41
+ return implode(' ', $parts);
42
+ }
43
+
44
+ public function getUserPhone()
45
+ {
46
+ $address = Mage::getSingleton('checkout/session')->getQuote()->getBillingAddress();
47
+ return $address->getTelephone();
48
+ }
49
+
50
+ public function getUserEmail()
51
+ {
52
+ return Mage::getSingleton('checkout/session')->getQuote()->getCustomerEmail();
53
+ }
54
+
55
+ }
app/code/community/FermoPoint/StorePickup/Exception.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Exception extends Mage_Core_Exception {
4
+
5
+ }
app/code/community/FermoPoint/StorePickup/Helper/Config.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Helper_Config extends Mage_Core_Helper_Abstract
4
+ {
5
+
6
+ const API_VERSION = '0.9';
7
+
8
+ const ENDPOINT_PRODUCTION = 'http://api.fermopoint.it/api/:api_version/:api_method';
9
+ const ENDPOINT_SANDBOX = 'http://sandbox.fermopoint.it/api/v:api_version/:api_method';
10
+
11
+ const XML_PATH_ACCEPT = 'carriers/fpstorepickup/accept';
12
+ const XML_PATH_COST = 'carriers/fpstorepickup/cost';
13
+ const XML_PATH_SANDBOX = 'carriers/fpstorepickup/sandbox';
14
+ const XML_PATH_DEBUG = 'carriers/fpstorepickup/debug';
15
+ const XML_PATH_CLIENTID = 'carriers/fpstorepickup/client_id';
16
+ const XML_PATH_CLIENTSECRET = 'carriers/fpstorepickup/client_secret';
17
+ const XML_PATH_TOSURL = 'carriers/fpstorepickup/tos_url';
18
+ const XML_PATH_GMAPSKEY = 'carriers/fpstorepickup/gmaps_key';
19
+ const XML_PATH_ALLOWSPECIFIC = 'carriers/fpstorepickup/allowspecific_payment';
20
+ const XML_PATH_SPECIFICPAYMENTS = 'carriers/fpstorepickup/specificpayment';
21
+
22
+ public function getTosAccepted()
23
+ {
24
+ return Mage::getStoreConfigFlag(self::XML_PATH_ACCEPT);
25
+ }
26
+
27
+ public function getClientId()
28
+ {
29
+ return (string) Mage::getStoreConfig(self::XML_PATH_CLIENTID);
30
+ }
31
+
32
+ public function getAllowSpecificPayments()
33
+ {
34
+ return Mage::getStoreConfig(self::XML_PATH_ALLOWSPECIFIC);
35
+ }
36
+
37
+ public function getSpecificPayments()
38
+ {
39
+ return explode(',', Mage::getStoreConfig(self::XML_PATH_SPECIFICPAYMENTS));
40
+ }
41
+
42
+ public function getClientSecret()
43
+ {
44
+ return (string) Mage::getStoreConfig(self::XML_PATH_CLIENTSECRET);
45
+ }
46
+
47
+ public function isSandbox()
48
+ {
49
+ return Mage::getStoreConfigFlag(self::XML_PATH_SANDBOX);
50
+ }
51
+
52
+ public function isDebug()
53
+ {
54
+ return Mage::getStoreConfigFlag(self::XML_PATH_DEBUG);
55
+ }
56
+
57
+ public function getCost()
58
+ {
59
+ return (float) Mage::getStoreConfig(self::XML_PATH_COST);
60
+ }
61
+
62
+ public function getTosUrl()
63
+ {
64
+ return Mage::getStoreConfig(self::XML_PATH_TOSURL);
65
+ }
66
+
67
+ public function getGMapsKey()
68
+ {
69
+ return Mage::getStoreConfig(self::XML_PATH_GMAPSKEY);
70
+ }
71
+
72
+ public function getEndpointUrl($method)
73
+ {
74
+ return strtr( ! $this->isSandbox() ? self::ENDPOINT_PRODUCTION : self::ENDPOINT_SANDBOX, array(
75
+ ':api_version' => self::API_VERSION,
76
+ ':api_method' => $method,
77
+ ));
78
+ }
79
+
80
+ }
app/code/community/FermoPoint/StorePickup/Helper/Data.php ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Helper_Data extends Mage_Core_Helper_Abstract
4
+ {
5
+
6
+ const SESSION_KEY = 'fermopoint_storepickup';
7
+
8
+ protected function _getSessionData($key, $default = null)
9
+ {
10
+ $session = Mage::getSingleton('checkout/session');
11
+ $data = $session->getData(self::SESSION_KEY);
12
+ if (is_array($data) && isset($data[$key]))
13
+ return $data[$key];
14
+ else
15
+ return $default;
16
+ }
17
+
18
+ protected function _setSessionData($key, $val)
19
+ {
20
+ $session = Mage::getSingleton('checkout/session');
21
+ $data = $session->getData(self::SESSION_KEY);
22
+ if ( ! is_array($data))
23
+ $data = array();
24
+ $data[$key] = $val;
25
+ $session->setData(self::SESSION_KEY, $data);
26
+ }
27
+
28
+
29
+ public function setUseMethod($flag)
30
+ {
31
+ $this->_setSessionData('use_storepickup', $flag);
32
+ }
33
+
34
+ public function getUseMethod()
35
+ {
36
+ return $this->_getSessionData('use_storepickup', false);
37
+ }
38
+
39
+ public function setPointId($pointId)
40
+ {
41
+ $this->setUseMethod(true);
42
+ $this->_setSessionData('point_id', $pointId);
43
+ }
44
+
45
+ public function getPointId()
46
+ {
47
+ return $this->_getSessionData('point_id', 0);
48
+ }
49
+
50
+ public function setAccountType($type)
51
+ {
52
+ $this->_setSessionData('account', $type);
53
+ }
54
+
55
+ public function getAccountType()
56
+ {
57
+ return $this->_getSessionData('account', 'new');
58
+ }
59
+
60
+ public function setNickname($nickname)
61
+ {
62
+ $this->_setSessionData('nickname', $nickname);
63
+ }
64
+
65
+ public function getNickname()
66
+ {
67
+ return $this->_getSessionData('nickname', '');
68
+ }
69
+
70
+ public function setPhoneNumber($number)
71
+ {
72
+ $this->_setSessionData('phone_number', $number);
73
+ }
74
+
75
+ public function getPhoneNumber()
76
+ {
77
+ return $this->_getSessionData('phone_number', '');
78
+ }
79
+
80
+ public function getChangeMethodUrl()
81
+ {
82
+ return $this->_getUrl('fpstorepickup/index/changemethod', array('_secure' => true));
83
+ }
84
+
85
+ public function getSearchUrl()
86
+ {
87
+ return $this->_getUrl('fpstorepickup/index/search', array('_secure' => true));
88
+ }
89
+
90
+ public function getLocationUrl()
91
+ {
92
+ return $this->_getUrl('fpstorepickup/index/location', array('_secure' => true));
93
+ }
94
+
95
+ public function getMediaUrl()
96
+ {
97
+ return Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA, true) . 'fermopoint/';
98
+ }
99
+
100
+ public function getCustomerAddress()
101
+ {
102
+ $cSession = Mage::getSingleton('customer/session');
103
+
104
+ $attribute = Mage::getModel("eav/entity_attribute")->load("customer_shipping_address_id","attribute_code");
105
+
106
+ if($cSession->isLoggedIn() && $attribute->getId())
107
+ {
108
+ $address = Mage::helper('accountfield')
109
+ ->getShippingAddressByCustomerId($cSession->getCustomer()->getId());
110
+ if($address)
111
+ return $address;
112
+ }
113
+
114
+ $cart = Mage::getSingleton('checkout/cart');
115
+ return $cart->getQuote()->getShippingAddress();
116
+ }
117
+ }
app/code/community/FermoPoint/StorePickup/Model/Api.php ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Model_Api {
4
+
5
+ protected function _hideClientSecret($debugData, $clientSecret)
6
+ {
7
+ if (is_array($debugData) && strlen($clientSecret)) {
8
+ foreach ($debugData as $key => &$value) {
9
+ if (is_string($value))
10
+ $value = str_replace($clientSecret, '******', $value);
11
+ else
12
+ $value = $this->_hideClientSecret($value, $clientSecret);
13
+ }
14
+ }
15
+ return $debugData;
16
+ }
17
+
18
+ protected function _debug($debugData)
19
+ {
20
+ if (Mage::helper('fpstorepickup/config')->isDebug()) {
21
+ $debugData = $this->_hideClientSecret($debugData, Mage::helper('fpstorepickup/config')->getClientSecret());
22
+ Mage::getModel('core/log_adapter', 'fermopoint_storepickup.log')
23
+ ->log($debugData);
24
+ }
25
+ }
26
+
27
+ protected function _getToken($merchantId, $clientSecret, $body)
28
+ {
29
+ return hash_hmac("sha256", $body, $merchantId . $clientSecret);
30
+ }
31
+
32
+ protected function _signData($data, $clientId, $clientSecret)
33
+ {
34
+ if (empty($clientId))
35
+ throw new FermoPoint_StorePickup_Exception(Mage::helper('fpstorepickup')->__('Please specify Client ID'));
36
+ if (empty($clientSecret))
37
+ throw new FermoPoint_StorePickup_Exception(Mage::helper('fpstorepickup')->__('Please specify Client Key'));
38
+ $ts = gmdate('Y-m-d\TH:i:s') . substr((string) microtime(), 1, 8) . 'Z';
39
+ return array(
40
+ 'client_id' => $clientId,
41
+ 'ts' => $ts,
42
+ 'auth_token' => hash_hmac('sha256', $ts, $clientId . $clientSecret),
43
+ 'data' => $data,
44
+ );
45
+ }
46
+
47
+ protected function _buildUrl($base, $params)
48
+ {
49
+ return $base
50
+ . (strpos($base, '?') !== false ? '' : '?')
51
+ . http_build_query($params)
52
+ ;
53
+ }
54
+
55
+ public function call($method, $data = array(), $params = array())
56
+ {
57
+ $config = Mage::helper('fpstorepickup/config');
58
+ $signedData = $this->_signData($data, $config->getClientId(), $config->getClientSecret());
59
+ $this->_debug($signedData);
60
+
61
+ $client = new Zend_Http_Client($this->_buildUrl($config->getEndpointUrl($method), $params));
62
+ $client->setConfig(array(
63
+ 'maxredirects' => 0,
64
+ 'timeout' => 15,
65
+ ));
66
+ $client->setHeaders(array('Accept: text/json'));
67
+ $client->setHeaders(array('Accept-Encoding: identity'));
68
+ $client->setConfig(array('strictredirects' => true));
69
+ $client->setRawData(json_encode($signedData), 'application/json');
70
+
71
+ try {
72
+ $response = $client->request(Zend_Http_Client::POST);
73
+ } catch (Zend_Http_Client_Exception $e) {
74
+ Mage::logException($e);
75
+ throw new FermoPoint_StorePickup_Exception(Mage::helper('fpstorepickup')->__('Could not communicate with server'));
76
+ }
77
+ $body = $response->getBody();
78
+
79
+ $json = json_decode($body, true);
80
+ if ($json === null)
81
+ {
82
+ $this->_debug($body);
83
+ throw new FermoPoint_StorePickup_Exception(Mage::helper('fpstorepickup')->__('Invalid server reply'));
84
+ }
85
+
86
+ $this->_debug($json);
87
+
88
+ return $json;
89
+ }
90
+
91
+ public function getMerchant()
92
+ {
93
+ return $this->call('merchant');
94
+ }
95
+
96
+ public function validateMerchant()
97
+ {
98
+ $result = $this->call('merchant');
99
+
100
+ return isset($result['credits']) && $result['credits'] > 0;
101
+ }
102
+
103
+ public function getOrdersCount()
104
+ {
105
+ $raw = $this->call('merchant');
106
+ $result = 0;
107
+ if (isset($raw['orders']))
108
+ foreach ($raw['orders'] as $order)
109
+ $result += $order['count'];
110
+ return $result;
111
+ }
112
+
113
+ public function isNicknameAvailable($nickname)
114
+ {
115
+ return $this->call('users/nickname', array(
116
+ 'nickname' => $nickname,
117
+ ));
118
+ }
119
+
120
+ public function isEmailAvailable($email)
121
+ {
122
+ return $this->call('users/email', array(
123
+ 'email' => $email,
124
+ ));
125
+ }
126
+
127
+ public function getPoints($params)
128
+ {
129
+ $points = $this->call('points/search', $params);
130
+ if ( ! is_array($points))
131
+ return array();
132
+
133
+ return $points;
134
+ }
135
+
136
+ public function submitOrder(Mage_Sales_Model_Order $order, FermoPoint_StorePickup_Model_Order_Point $orderPoint)
137
+ {
138
+ $data = array(
139
+ 'point_id' => $orderPoint->getPointId(),
140
+ 'merchant_id' => $order->getIncrementId(),
141
+ 'merchant_notes' => Mage::helper('fpstorepickup')->__('Order #%s', $order->getIncrementId()),
142
+ 'existing_user' => $orderPoint->getAccountType() == 'existing',
143
+ 'nickname' => $orderPoint->getNickname(),
144
+ 'email' => $orderPoint->getEmail(),
145
+ 'phone_number' => $orderPoint->getPhoneNumber(),
146
+ );
147
+ if ( ! $data['existing_user'])
148
+ {
149
+ $billingAddress = $order->getBillingAddress();
150
+ $regionCode = null;
151
+ if ($regionId = $billingAddress->getRegionId())
152
+ {
153
+ $region = Mage::getModel('directory/region')->load($regionId);
154
+ if ($region->getId())
155
+ $regionCode = $region->getCode();
156
+ }
157
+ if ( ! isset($regionCode))
158
+ $regionCode = $billingAddress->getRegion();
159
+
160
+ $data['user'] = array(
161
+ 'nickname' => $orderPoint->getNickname(),
162
+ 'email' => $orderPoint->getEmail(),
163
+ 'phone_number' => $orderPoint->getPhoneNumber(),
164
+ 'full_name' => $order->getCustomerName(),
165
+ 'address' => array(
166
+ 's' => $billingAddress->getStreetFull(),
167
+ 'e' => null,
168
+ 'c' => $billingAddress->getCity(),
169
+ 'l' => null,
170
+ 'p' => $billingAddress->getPostcode(),
171
+ 'd' => $regionCode,
172
+ ),
173
+ //'born_date' => '1988-01-01',
174
+ //'tax_code' => 'AAAAAA11A11A111A',
175
+ 'newsletter' => true,
176
+ );
177
+ }
178
+ $result = $this->call('booking/book', $data);
179
+
180
+ if ( ! is_array($result) || ! isset($result['ticketId']))
181
+ throw new FermoPoint_StorePickup_Exception(Mage::helper('fpstorepickup')->__('Could not submit order'));
182
+
183
+ return $result['ticketId'];
184
+ }
185
+
186
+ protected function _parseTracking($data)
187
+ {
188
+ return array(
189
+ 'status' => $data['state'],
190
+ 'datetime' => $data['last_update'],
191
+ 'history' => isset($data['notes']) && is_array($data['notes']) ? $data['notes'] : array(),
192
+ );
193
+ return $data;
194
+ }
195
+
196
+ public function trackShipment($trackNumbers)
197
+ {
198
+ $result = array();
199
+ foreach ($trackNumbers as $trackNumber)
200
+ {
201
+ try {
202
+ $result[$trackNumber] = $this->_parseTracking($this->call('orders/order/' . $trackNumber));
203
+ } catch (FermoPoint_StorePickup_Exception $e) {
204
+ Mage::logException($e);
205
+ $result[$trackNumber] = $e->getMessage();
206
+ }
207
+ }
208
+ return $result;
209
+ }
210
+
211
+ public function approveOrderByTicketId($ticketId)
212
+ {
213
+ $this->call('booking/approve/' . $ticketId);
214
+ }
215
+
216
+ public function cancelOrderByTicketId($ticketId)
217
+ {
218
+ $this->call('booking/cancel/' . $ticketId);
219
+ }
220
+
221
+ public function getOrders($limit = 20, $offset = 0)
222
+ {
223
+ return $this->call('orders', array(), array('take' => $limit, 'skip' => $offset));
224
+ }
225
+
226
+ }
app/code/community/FermoPoint/StorePickup/Model/Api/OrderCollection.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Model_Api_OrderCollection extends Varien_Data_Collection {
4
+
5
+ protected $_size = null;
6
+
7
+ public function getSize()
8
+ {
9
+ if ($this->_size === null)
10
+ {
11
+ try {
12
+ $this->_size = Mage::getSingleton('fpstorepickup/api')->getOrdersCount();
13
+ } catch (FermoPoint_StorePickup_Exception $e) {
14
+ $this->_size = 1;
15
+ }
16
+ }
17
+ return $this->_size;
18
+ }
19
+
20
+ public function loadData($printQuery = false, $logQuery = false)
21
+ {
22
+ if ($this->isLoaded()) {
23
+ return $this;
24
+ }
25
+ $limit = $this->getPageSize();
26
+ $offset = $this->getCurPage();
27
+ try {
28
+ $raw = Mage::getSingleton('fpstorepickup/api')->getOrders($limit, ($offset - 1) * $limit);
29
+ } catch (FermoPoint_StorePickup_Exception $e) {
30
+ $raw = array(
31
+ array(
32
+ 'email' => Mage::helper('fpstorepickup')->__('Service is not available at the moment, please try again later'),
33
+ )
34
+ );
35
+ }
36
+ foreach ($raw as $row)
37
+ {
38
+ $obj = new Varien_Object();
39
+ $obj->setData($row);
40
+ $this->addItem($obj);
41
+ }
42
+ $this->_setIsLoaded();
43
+ return $this;
44
+ }
45
+
46
+ }
app/code/community/FermoPoint/StorePickup/Model/Api/SearchData.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Model_Api_SearchData extends Varien_Object {
4
+
5
+ protected function _validate()
6
+ {
7
+ if ( ! Zend_Validate::is($this->getAddress(), 'NotEmpty')
8
+ || ! Zend_Validate::is($this->getRadius(), 'NotEmpty')
9
+ )
10
+ Mage::throwException(Mage::helper('fpstorepickup')->__('Invalid search parameters.'));
11
+
12
+ return $this;
13
+ }
14
+
15
+ protected function _searchLocation()
16
+ {
17
+ $location = Mage::getSingleton('fpstorepickup/googleMaps')->getLocation($this->getAddress());
18
+ if ($location === null)
19
+ Mage::throwException(Mage::helper('fpstorepickup')->__('Location does not found'));
20
+ list($lat, $lng) = $location;
21
+ $this->setLatitude($lat);
22
+ $this->setLongitude($lng);
23
+
24
+ return $this;
25
+ }
26
+
27
+ public function loadFromPost($raw)
28
+ {
29
+ $this
30
+ ->setData($raw)
31
+ ->_validate()
32
+ ->_searchLocation();
33
+
34
+ return $this;
35
+ }
36
+
37
+ public function compare($with)
38
+ {
39
+ $src = $this->toApi();
40
+ $result = true;
41
+ foreach ($src as $key => $val)
42
+ if ( ! isset($with[$key]) || $with[$key] != $val)
43
+ {
44
+ $result = false;
45
+ break;
46
+ }
47
+ return $result;
48
+ }
49
+
50
+ public function toApi()
51
+ {
52
+ $result = array(
53
+ 'lat' => $this->getLatitude(),
54
+ 'lng' => $this->getLongitude(),
55
+ 'radius' => $this->getRadius(),
56
+ 'day' => (int) $this->getDay(),
57
+ 'from' => (int) $this->getFrom(),
58
+ 'to' => $this->getTo() ? (int) $this->getTo() : 24,
59
+ );
60
+
61
+ return $result;
62
+ }
63
+
64
+ }
app/code/community/FermoPoint/StorePickup/Model/Carrier/Storepickup.php ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Model_Carrier_Storepickup
4
+ extends Mage_Shipping_Model_Carrier_Abstract
5
+ implements Mage_Shipping_Model_Carrier_Interface
6
+ {
7
+
8
+ protected $_code = 'fpstorepickup';
9
+
10
+
11
+ public function getCode()
12
+ {
13
+ return $this->_code;
14
+ }
15
+
16
+ public function collectRates(Mage_Shipping_Model_Rate_Request $request)
17
+ {
18
+ if ( ! $this->getConfigFlag('active'))
19
+ return false;
20
+
21
+ if ( ! Mage::helper('fpstorepickup/config')->getTosAccepted())
22
+ return false;
23
+
24
+ $items = $request->getAllItems();
25
+ if ( ! count($items))
26
+ return false;
27
+
28
+ if ($request->getDestCountryId() !== 'IT')
29
+ return false;
30
+
31
+ if ($request->getPackageWeight() > $this->getConfigData('maximum_weight'))
32
+ return false;
33
+
34
+ if ($request->getBaseSubtotalInclTax() > $this->getConfigData('maximum_subtotal'))
35
+ return false;
36
+
37
+ $api = Mage::getSingleton('fpstorepickup/api');
38
+ try {
39
+ $result = $api->validateMerchant();
40
+ } catch (FermoPoint_StorePickup_Exception $e) {
41
+ return false;
42
+ }
43
+ if ( ! $result)
44
+ return false;
45
+
46
+ $cost = Mage::helper('fpstorepickup/config')->getCost();
47
+
48
+ $result = Mage::getModel('shipping/rate_result');
49
+ $method = Mage::getModel('shipping/rate_result_method');
50
+ $method->setCarrier('fpstorepickup');
51
+ $method->setCarrierTitle($this->getConfigData('title'));
52
+ $method->setMethod('fpstorepickup');
53
+ $method->setMethodTitle('');
54
+ $method->setPrice($cost);
55
+ $method->setCost($cost);
56
+ $result->append($method);
57
+
58
+ return $result;
59
+ }
60
+
61
+ public function getAllowedMethods()
62
+ {
63
+ return array(
64
+ 'fpstorepickup' => 'fpstorepickup',
65
+ );
66
+ }
67
+
68
+ public function getTrackingInfo($tracking)
69
+ {
70
+ $info = array();
71
+
72
+ $result = $this->getTracking($tracking);
73
+
74
+ if($result instanceof Mage_Shipping_Model_Tracking_Result){
75
+ if ($trackings = $result->getAllTrackings()) {
76
+ return $trackings[0];
77
+ }
78
+ }
79
+ elseif (is_string($result) && !empty($result)) {
80
+ return $result;
81
+ }
82
+
83
+ return false;
84
+ }
85
+
86
+ public function getTracking($trackings)
87
+ {
88
+ if ( ! is_array($trackings))
89
+ $trackings = array($trackings);
90
+
91
+ $errorTitle = Mage::helper('fpstorepickup')->__('Unable to retrieve tracking');
92
+
93
+ $raw = Mage::getSingleton('fpstorepickup/api')->trackShipment($trackings);
94
+
95
+ $success = array();
96
+ $error = array();
97
+ foreach ($raw as $idx => $row)
98
+ if (is_array($row))
99
+ {
100
+ $progress = array();
101
+ foreach ($row['history'] as $entry)
102
+ {
103
+ $timestamp = strtotime($entry['date']);
104
+ $progress[] = array(
105
+ 'deliverydate' => date('Y-m-d', $timestamp),
106
+ 'deliverytime' => date('H:i:s', $timestamp),
107
+ 'activity' => $entry['note'],
108
+ 'deliverylocation' => Mage::helper('fpstorepickup')->__('Fermo!Point'),
109
+ );
110
+ }
111
+ $timestamp = strtotime($row['datetime']);
112
+ $success[$idx] = array(
113
+ 'deliverydate' => date('Y-m-d', $timestamp),
114
+ 'deliverytime' => date('H:i:s', $timestamp),
115
+ 'status' => Mage::helper('fpstorepickup')->__($row['status']),
116
+ 'progressdetail' => $progress,
117
+ );
118
+ }
119
+ else
120
+ $error[$idx] = true;
121
+
122
+ $result = Mage::getModel('shipping/tracking_result');
123
+ if ($success || $error) {
124
+ foreach ($error as $t => $r) {
125
+ $error = Mage::getModel('shipping/tracking_result_error');
126
+ $error->setCarrier('fpstorepickup');
127
+ $error->setCarrierTitle($this->getConfigData('title'));
128
+ $error->setTracking($t);
129
+ $error->setErrorMessage($errorTitle);
130
+ $result->append($error);
131
+ }
132
+
133
+ foreach ($success as $t => $data) {
134
+ $tracking = Mage::getModel('shipping/tracking_result_status');
135
+ $tracking->setCarrier('fpstorepickup');
136
+ $tracking->setCarrierTitle($this->getConfigData('title'));
137
+ $tracking->setTracking($t);
138
+ $tracking->addData($data);
139
+
140
+ $result->append($tracking);
141
+ }
142
+ } else {
143
+ foreach ($trackings as $t) {
144
+ $error = Mage::getModel('shipping/tracking_result_error');
145
+ $error->setCarrier('fpstorepickup');
146
+ $error->setCarrierTitle($this->getConfigData('title'));
147
+ $error->setTracking($t);
148
+ $error->setErrorMessage($errorTitle);
149
+ $result->append($error);
150
+
151
+ }
152
+ }
153
+
154
+ return $result;
155
+ }
156
+
157
+ }
app/code/community/FermoPoint/StorePickup/Model/GoogleMaps.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Model_GoogleMaps
4
+ {
5
+ const GEOCODER_URL = 'http://maps.google.com/maps/api/geocode/json?';
6
+ const STATUS_OK = 'ok';
7
+
8
+ const CACHE_TAG = 'fermopoint_gmaps';
9
+ const CACHE_KEY = 'fermopoint_gmaps_location_%s';
10
+ const CACHE_LIFETIME = 604800; // 1 week
11
+ const CACHE_LIFETIME_ERROR = 300; // 5 mins
12
+
13
+ protected function getLocationFromGoogle($address)
14
+ {
15
+ $params = array(
16
+ 'sensor' => 'false',
17
+ 'address' => $address,
18
+ );
19
+ if ($apiKey = Mage::helper('fpstorepickup/config')->getGMapsKey())
20
+ $params['key'] = $apiKey;
21
+
22
+ $url = self::GEOCODER_URL . http_build_query($params);
23
+
24
+ $client = new Zend_Http_Client($url);
25
+ $client->setHeaders(array('Accept-encoding: identity'));
26
+ $client->setConfig(array('strictredirects' => true));
27
+
28
+ try {
29
+ $response = $client->request(Zend_Http_Client::POST);
30
+ } catch (Zend_Http_Client_Exception $e) {
31
+ Mage::logException($e);
32
+ return null;
33
+ }
34
+
35
+ $body = $response->getBody();
36
+ $json = json_decode($body, true);
37
+ if ($json === null)
38
+ return null;
39
+
40
+ if ( ! isset($json['status']) || strtolower($json['status']) !== self::STATUS_OK)
41
+ return null;
42
+
43
+ if ( ! isset($json['results']) || ! is_array($json['results']) || ! count($json['results']))
44
+ return null;
45
+
46
+ $result = reset($json['results']);
47
+ return array(
48
+ $result['geometry']['location']['lat'],
49
+ $result['geometry']['location']['lng'],
50
+ );
51
+ }
52
+
53
+ public function getLocation($address)
54
+ {
55
+ $cacheKey = sprintf(self::CACHE_KEY, md5($address));
56
+ $cache = Mage::app()->getCache();
57
+ $value = unserialize($cache->load($cacheKey));
58
+ if ( ! is_array($value) || $value['address'] != $address)
59
+ {
60
+ $value = array(
61
+ 'address' => $address,
62
+ 'location' => $this->getLocationFromGoogle($address),
63
+ );
64
+
65
+ $cache->save(
66
+ serialize($value),
67
+ $cacheKey,
68
+ array(self::CACHE_TAG),
69
+ $value['location'] !== null ? self::CACHE_LIFETIME : self::CACHE_LIFETIME_ERROR
70
+ );
71
+ }
72
+ return $value['location'];
73
+ }
74
+
75
+ }
app/code/community/FermoPoint/StorePickup/Model/Observer.php ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Model_Observer
4
+ {
5
+
6
+ public function getQuote()
7
+ {
8
+ return Mage::getSingleton('checkout/session')->getQuote();
9
+ }
10
+
11
+ protected function _returnError($controller, $message)
12
+ {
13
+ $result = array(
14
+ 'error' => -1,
15
+ 'message' => $message,
16
+ );
17
+ $controller->getOnepage()->getQuote()->collectTotals()->save();
18
+ $controller->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
19
+ $controller->setFlag('', Mage_Core_Controller_Varien_Action::FLAG_NO_DISPATCH, true);
20
+ }
21
+
22
+ public function onSaveShippingMethodBefore($observer)
23
+ {
24
+ $controller = $observer->getEvent()->getControllerAction();
25
+ $request = $controller->getRequest();
26
+ if ( ! $request->isPost())
27
+ return;
28
+
29
+ if ( ! Mage::helper('fpstorepickup')->getUseMethod(false))
30
+ return;
31
+
32
+ $pointId = $request->getPost('fermopoint_point_id', 0);
33
+ $point = Mage::getModel('fpstorepickup/point')->load($pointId);
34
+ if ( ! $point->getId())
35
+ return $this->_returnError($controller, Mage::helper('fpstorepickup')->__('Unknown Fermo!Point ID'));
36
+
37
+ $nickname = trim($request->getPost('fermopoint_nickname', ''));
38
+ if (empty($nickname))
39
+ return $this->_returnError($controller, Mage::helper('fpstorepickup')->__('Invalid Nickname'));
40
+
41
+ $api = Mage::getModel('fpstorepickup/api');
42
+ switch ($accountType = $request->getPost('fermopoint_account', 'new'))
43
+ {
44
+ case 'existing':
45
+ if ($api->isNicknameAvailable($nickname))
46
+ return $this->_returnError($controller, Mage::helper('fpstorepickup')->__('There is no user with this nickname'));
47
+ break;
48
+
49
+ case 'new':
50
+ default:
51
+ $email = $this->getQuote()->getCustomerEmail();
52
+ if ( ! $api->isEmailAvailable($email))
53
+ return $this->_returnError($controller, Mage::helper('fpstorepickup')->__('Your email is already registered on Fermo!Point'));
54
+
55
+ if ( ! $api->isNicknameAvailable($nickname))
56
+ return $this->_returnError($controller, Mage::helper('fpstorepickup')->__('User with this nickname already exists'));
57
+ }
58
+
59
+ /*$telephone = trim($request->getPost('fermopoint_phone', ''));
60
+ if (empty($telephone))
61
+ return $this->_returnError($controller, Mage::helper('fpstorepickup')->__('Invalid phone number'));
62
+ */
63
+ }
64
+
65
+ public function onSaveShippingMethodAfter($event)
66
+ {
67
+ $quote = $event->getQuote();
68
+ if ($quote->getShippingAddress()->getShippingMethod() !== 'fpstorepickup_fpstorepickup')
69
+ {
70
+ Mage::helper('fpstorepickup')->setUseMethod(false);
71
+ return;
72
+ }
73
+ $request = $event->getRequest();
74
+ $pointId = $request->getPost('fermopoint_point_id');
75
+ Mage::helper('fpstorepickup')->setPointId($pointId);
76
+ Mage::helper('fpstorepickup')->setNickname(trim($request->getPost('fermopoint_nickname')));
77
+ Mage::helper('fpstorepickup')->setAccountType(trim($request->getPost('fermopoint_account')));
78
+ Mage::helper('fpstorepickup')->setPhoneNumber(trim($request->getPost('fermopoint_phone')));
79
+
80
+ $point = Mage::getSingleton('fpstorepickup/points')->getPoint($pointId);
81
+ if ($point->getId())
82
+ {
83
+ $billingAddress = $quote->getBillingAddress();
84
+ $address = $quote->getShippingAddress();
85
+ $address->addData($point->getAddressData());
86
+ $address->setLastname('c/o ' . $address->getLastname() . ' - ' . $address->getFirstname());
87
+ $address->setFirstname(Mage::helper('fpstorepickup')->getNickname());
88
+ $address->setTelephone($billingAddress->getTelephone());
89
+
90
+ $address->implodeStreetAddress();
91
+ $address->setCollectShippingRates(true);
92
+
93
+ $quote->collectTotals()->save();
94
+ }
95
+ }
96
+
97
+ public function onSaveOrderAfter($event)
98
+ {
99
+ $order = $event->getOrder();
100
+ $orderPoint = Mage::getModel('fpstorepickup/order_point')->load($order->getId(), 'order_id');
101
+ if ($orderPoint->getId())
102
+ return;
103
+
104
+ $shippingMethod = $order->getShippingMethod();
105
+ if ($shippingMethod !== 'fpstorepickup_fpstorepickup')
106
+ return;
107
+
108
+ $pointId = Mage::helper('fpstorepickup')->getPointId();
109
+ if ( ! $pointId)
110
+ return;
111
+
112
+ $options = array(
113
+ 'account_type' => Mage::helper('fpstorepickup')->getAccountType(),
114
+ 'nickname' => Mage::helper('fpstorepickup')->getNickname(),
115
+ 'phone_number' => Mage::helper('fpstorepickup')->getPhoneNumber(),
116
+ 'email' => $order->getCustomerEmail(),
117
+ );
118
+ $point = Mage::getSingleton('fpstorepickup/points')->getPoint($pointId);
119
+ $orderPoint = Mage::getModel('fpstorepickup/order_point')
120
+ ->createFrom($order, $point, $options)
121
+ ;
122
+ try {
123
+ $orderPoint
124
+ ->setTicketId(Mage::getSingleton('fpstorepickup/api')->submitOrder($order, $orderPoint))
125
+ ->save()
126
+ ;
127
+ } catch (FermoPoint_StorePickup_Exception $e) {
128
+ Mage::logException($e);
129
+ return;
130
+ }
131
+
132
+ Mage::helper('fpstorepickup')->setUseMethod(false);
133
+ }
134
+
135
+ protected function _insertRadioJs(Mage_Core_Block_Abstract $block, Varien_Object $transport)
136
+ {
137
+ $html = $transport->getHtml();
138
+ if ( ! preg_match('#(<ul[^>]+>.+?use_for_shipping.+?)</ul>#ius', $html, $matches))
139
+ return;
140
+
141
+ $html = str_replace(
142
+ $matches[1],
143
+ $matches[1] . $block->getLayout()->createBlock('fpstorepickup/checkout_billing_radio')->toHtml(),
144
+ $html
145
+ );
146
+ $html .= $block->getLayout()->createBlock('fpstorepickup/checkout_billing_js')->toHtml();
147
+ $transport->setHtml($html);
148
+ }
149
+
150
+ public function onBlockToHtmlAfter($event)
151
+ {
152
+ if ( ! Mage::getStoreConfig('carriers/fpstorepickup/active')
153
+ || ! Mage::getStoreConfig('carriers/fpstorepickup/accept')
154
+ )
155
+ return;
156
+
157
+ $block = $event->getBlock();
158
+ if ( ! $block)
159
+ return;
160
+
161
+ if ($block->getType() == 'checkout/onepage_billing')
162
+ $this->_insertRadioJs($block, $event->getTransport());
163
+ }
164
+
165
+ public function onOrderInvoicePay($event)
166
+ {
167
+ $invoice = $event->getInvoice();
168
+ $order = $invoice->getOrder();
169
+
170
+ $orderId = $order->getId();
171
+ $orderPoint = Mage::getModel('fpstorepickup/order_point')->load($orderId, 'order_id');
172
+ if ( ! $orderPoint->getId() || $orderPoint->getIsApproved() || ! $orderPoint->getTicketId())
173
+ return;
174
+
175
+ $ticketId = $orderPoint->getTicketId();
176
+ try {
177
+ Mage::getSingleton('fpstorepickup/api')->approveOrderByTicketId($ticketId);
178
+ } catch (FermoPoint_StorePickup_Exception $e) {
179
+ Mage::logException($e);
180
+ return;
181
+ }
182
+
183
+ $orderPoint
184
+ ->setIsApproved(true)
185
+ ->save()
186
+ ;
187
+
188
+ if ( ! $order->canShip())
189
+ return;
190
+
191
+ try {
192
+ $shipment = $order->prepareShipment();
193
+ if ($shipment)
194
+ {
195
+ $shipment->register();
196
+ $shipment->getOrder()->setIsInProcess(true);
197
+
198
+ $track = Mage::getModel('sales/order_shipment_track')
199
+ ->setShipment($shipment)
200
+ ->setData('title', Mage::helper('fpstorepickup')->__('Fermo!Point'))
201
+ ->setData('number', $ticketId)
202
+ ->setData('carrier_code', 'fpstorepickup')
203
+ ->setData('order_id', $shipment->getData('order_id'))
204
+ ;
205
+
206
+ $transactionSave = Mage::getModel('core/resource_transaction')
207
+ ->addObject($shipment)
208
+ ->addObject($shipment->getOrder())
209
+ ->addObject($track)
210
+ ->save()
211
+ ;
212
+ }
213
+ } catch (Mage_Core_Exception $e) {
214
+ Mage::logException($e);
215
+ }
216
+ }
217
+
218
+ public function onOrderCancel($event)
219
+ {
220
+ $order = $event->getOrder();
221
+
222
+ $orderId = $order->getId();
223
+ $orderPoint = Mage::getModel('fpstorepickup/order_point')->load($orderId, 'order_id');
224
+ if ( ! $orderPoint->getId() || $orderPoint->getIsCancelled() || ! $orderPoint->getTicketId())
225
+ return;
226
+
227
+ $ticketId = $orderPoint->getTicketId();
228
+ try {
229
+ Mage::getSingleton('fpstorepickup/api')->cancelOrderByTicketId($ticketId);
230
+ } catch (FermoPoint_StorePickup_Exception $e) {
231
+ Mage::logException($e);
232
+ return;
233
+ }
234
+
235
+ $orderPoint
236
+ ->setIsCancelled(true)
237
+ ->save()
238
+ ;
239
+ }
240
+
241
+ }
app/code/community/FermoPoint/StorePickup/Model/Order/Point.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Model_Order_Point extends Mage_Core_Model_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ parent::_construct();
8
+ $this->_init('fpstorepickup/order_point');
9
+ }
10
+
11
+ public function createFrom(Mage_Sales_Model_Order $order, FermoPoint_StorePickup_Model_Point $point, $options)
12
+ {
13
+ $this->setData('order_id', $order->getId());
14
+ $this->setData('point_id', $point->getId());
15
+ $this->setData('point_name', $point->getName());
16
+ $this->setData('point_address', $point->getFormattedAddress());
17
+ $this->setData('point_data', $point->getData());
18
+ $this->setData('is_approved', false);
19
+ $this->setData('is_cancelled', false);
20
+ $this->addData($options);
21
+ $this->save();
22
+ return $this;
23
+ }
24
+
25
+ public function getPoint()
26
+ {
27
+ return Mage::getModel('fpstorepickup/point')->setData($this->getPointData());
28
+ }
29
+
30
+ }
app/code/community/FermoPoint/StorePickup/Model/Point.php ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Model_Point extends Mage_Core_Model_Abstract {
4
+
5
+ public function _construct()
6
+ {
7
+ parent::_construct();
8
+ $this->_init('fpstorepickup/point');
9
+ }
10
+
11
+ protected function _getPointData($index, $default = null)
12
+ {
13
+ $data = $this->getPointData();
14
+ return isset($data[$index]) ? $data[$index] : $default;
15
+ }
16
+
17
+ protected function _getPointAddressData($index, $default = null)
18
+ {
19
+ $address = $this->_getPointData('a', array());
20
+ return isset($address[$index]) ? $address[$index] : $default;
21
+ }
22
+
23
+ public function setPointData($data)
24
+ {
25
+ $this->setData('point_data', $data);
26
+ $this->setName($this->getName());
27
+ $this->setAddress($this->getFormattedAddress());
28
+ return $this;
29
+ }
30
+
31
+ public function getName()
32
+ {
33
+ return $this->_getPointData('n', 'Point #' . $this->getId());
34
+ }
35
+
36
+ public function getStreet()
37
+ {
38
+ $parts = array();
39
+ foreach (array('s', 'e') as $key)
40
+ {
41
+ $value = trim($this->_getPointAddressData($key));
42
+ if ( ! empty($value))
43
+ {
44
+ $value = ' ' . $value;
45
+ $parts[] = $value;
46
+ }
47
+ }
48
+ return trim(implode('', $parts));
49
+ }
50
+
51
+ public function getCity()
52
+ {
53
+ return $this->_getPointAddressData('c');
54
+ }
55
+
56
+ public function getPostcode()
57
+ {
58
+ return $this->_getPointAddressData('p');
59
+ }
60
+
61
+ public function getRegion()
62
+ {
63
+ return $this->_getPointAddressData('d');
64
+ }
65
+
66
+ public function getRegionId()
67
+ {
68
+ $regionModel = Mage::getModel('directory/region')->loadByCode($this->getRegion(), $this->getCountryId());
69
+ return $regionModel->getId();
70
+ }
71
+
72
+ public function getCountryId()
73
+ {
74
+ // hardcoded
75
+ return 'IT';
76
+ }
77
+
78
+ public function getLatitude()
79
+ {
80
+ return $this->_getPointData('lt');
81
+ }
82
+
83
+ public function getLongitude()
84
+ {
85
+ return $this->_getPointData('ln');
86
+ }
87
+
88
+ public function getDistance()
89
+ {
90
+ return $this->_getPointData('d');
91
+ }
92
+
93
+ protected function _formatDay($day)
94
+ {
95
+ $days = Mage::app()->getLocale()->getTranslationList('days');
96
+ $map = array(
97
+ 'sun',
98
+ 'mon',
99
+ 'tue',
100
+ 'wed',
101
+ 'thu',
102
+ 'fri',
103
+ 'sat',
104
+ );
105
+ return $days['format']['abbreviated'][$map[$day]];
106
+ }
107
+
108
+ protected function _formatHour($hour)
109
+ {
110
+ return sprintf('%02d:%02d', (int) $hour, abs($hour - (int) $hour) * 60);
111
+ }
112
+
113
+ public function getHours()
114
+ {
115
+ $rows = $this->_getPointData('o', array());
116
+ $result = array();
117
+ foreach ($rows as $row)
118
+ {
119
+ $hours = array();
120
+ foreach ($row['h'] as $range)
121
+ $hours[] = $this->_formatHour($range['o']) . '&ndash;' . $this->_formatHour($range['c']);
122
+ $result[] = array(
123
+ 'day' => $this->_formatDay($row['d']),
124
+ 'hours' => $hours,
125
+ );
126
+ }
127
+ return $result;
128
+ }
129
+
130
+ public function getContactPerson()
131
+ {
132
+ return $this->_getPointData('p');
133
+ }
134
+
135
+ public function getCategory()
136
+ {
137
+ return $this->_getPointData('s');
138
+ }
139
+
140
+ public function getAddressData()
141
+ {
142
+ return array(
143
+ 'firstname' => Mage::helper('fpstorepickup')->__('Fermo!Point'),
144
+ 'lastname' => $this->getName(),
145
+ 'company' => '',
146
+ 'street' => $this->getStreet(),
147
+ 'city' => $this->getCity(),
148
+ 'region' => $this->getRegion(),
149
+ 'region_id' => $this->getRegionId(),
150
+ 'postcode' => $this->getPostcode(),
151
+ 'country_id' => $this->getCountryId(),
152
+ 'telephone' => $this->getTelephone(),
153
+ );
154
+ }
155
+
156
+ public function getFormattedAddress()
157
+ {
158
+ $address = Mage::getModel('customer/address');
159
+ $address->setData($this->getAddressData());
160
+
161
+ return $address->format('oneline');
162
+ }
163
+
164
+ public function toArray(array $arrAttributes = array())
165
+ {
166
+ $result = array(
167
+ 'id' => $this->getId(),
168
+ 'name' => $this->getName(),
169
+ );
170
+ $result = array_merge($result, $this->getAddressData());
171
+ $result = array_merge($result, array(
172
+ 'latitude' => $this->getLatitude(),
173
+ 'longitude' => $this->getLongitude(),
174
+ 'distance' => $this->getDistance(),
175
+ 'hours' => $this->getHours(),
176
+ 'contact' => $this->getContactPerson(),
177
+ 'category' => $this->getCategory(),
178
+ ));
179
+
180
+ return $result;
181
+ }
182
+
183
+ }
app/code/community/FermoPoint/StorePickup/Model/Points.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Model_Points {
4
+
5
+ const CACHE_TAG = 'fermopoint_points';
6
+ const CACHE_KEY = 'fermopoint_points_%s';
7
+ const CACHE_LIFETIME = 3600; // 1 hour
8
+
9
+ protected function _storePoint($data)
10
+ {
11
+ $pointId = $data['id'];
12
+ $point = Mage::getModel('fpstorepickup/point')->load($pointId);
13
+ if ( ! $point->getId())
14
+ $point->setId($pointId);
15
+ $point
16
+ ->setPointData($data)
17
+ ->save()
18
+ ;
19
+ return $point;
20
+ }
21
+
22
+ public function getPoint($pointId)
23
+ {
24
+ $point = Mage::getModel('fpstorepickup/point')->load($pointId);
25
+ if ( ! $point->getId())
26
+ $point
27
+ ->setId($pointId)
28
+ ->setName('Point #' . $pointId)
29
+ ->save()
30
+ ;
31
+
32
+ return $point;
33
+ }
34
+
35
+ public function getPoints(FermoPoint_StorePickup_Model_Api_SearchData $request)
36
+ {
37
+ $needles = $request->toApi();
38
+ $cacheKey = sprintf(self::CACHE_KEY, sha1(implode(',', $needles)));
39
+ $cache = Mage::app()->getCache();
40
+ $value = unserialize($cache->load($cacheKey));
41
+ if ( ! is_array($value) || ! $request->compare($value))
42
+ {
43
+ $points = Mage::getSingleton('fpstorepickup/api')->getPoints($needles);
44
+ $value = $needles;
45
+ $value['points'] = array();
46
+ foreach ($points as $point)
47
+ $value['points'][] = $this->_storePoint($point)->toArray();
48
+ $cache->save(serialize($value), $cacheKey, array(self::CACHE_TAG), self::CACHE_LIFETIME);
49
+ }
50
+ return $value['points'];
51
+ }
52
+
53
+ }
app/code/community/FermoPoint/StorePickup/Model/Resource/Order/Point.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Model_Resource_Order_Point extends Mage_Core_Model_Mysql4_Abstract
4
+ {
5
+
6
+ protected $_serializableFields = array(
7
+ 'point_data' => array(
8
+ array(),
9
+ array(),
10
+ false
11
+ )
12
+ );
13
+
14
+ public function _construct()
15
+ {
16
+ $this->_init('fpstorepickup/order_point', 'order_point_id');
17
+ }
18
+ }
app/code/community/FermoPoint/StorePickup/Model/Resource/Order/Point/Collection.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Model_Resource_Order_Point_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ parent::_construct();
8
+ $this->_init('fpstorepickup/order_point');
9
+ }
10
+ }
app/code/community/FermoPoint/StorePickup/Model/Resource/Point.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Model_Resource_Point extends Mage_Core_Model_Mysql4_Abstract
4
+ {
5
+
6
+ protected $_isPkAutoIncrement = false;
7
+
8
+ protected $_serializableFields = array(
9
+ 'point_data' => array(
10
+ array(),
11
+ array(),
12
+ false
13
+ )
14
+ );
15
+
16
+ public function _construct()
17
+ {
18
+ $this->_init('fpstorepickup/point', 'point_id');
19
+ }
20
+ }
app/code/community/FermoPoint/StorePickup/Model/Resource/Point/Collection.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Model_Resource_Point_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ parent::_construct();
8
+ $this->_init('fpstorepickup/point');
9
+ }
10
+ }
app/code/community/FermoPoint/StorePickup/Model/Source/Payment.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Model_Source_Payment
4
+ {
5
+ public function toOptionArray()
6
+ {
7
+ $collection = Mage::getModel('payment/config')->getActiveMethods();
8
+
9
+ if ( ! count($collection))
10
+ return;
11
+
12
+ $options = array();
13
+ foreach ($collection as $item)
14
+ {
15
+ $title = $item->getTitle() ? $item->getTitle() : $item->getId();
16
+ $options[] = array('value' => $item->getId(), 'label' => $title);
17
+ }
18
+
19
+ return $options;
20
+ }
21
+ }
app/code/community/FermoPoint/StorePickup/Model/Source/Selectorpayment.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Model_Source_Selectorpayment
4
+ {
5
+ public function toOptionArray()
6
+ {
7
+ return array(
8
+ array('value' => 0, 'label' => Mage::helper('fpstorepickup')->__('All Allowed Payments')),
9
+ array('value' => 1, 'label' => Mage::helper('fpstorepickup')->__('Specific Payments')),
10
+ );
11
+ }
12
+ }
app/code/community/FermoPoint/StorePickup/Model/Source/State.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Model_Source_State {
4
+
5
+ protected $_options;
6
+
7
+ public function toOptionArray()
8
+ {
9
+ if ( ! $this->_options)
10
+ {
11
+ $this->_options = array(
12
+ array(
13
+ 'label' => Mage::helper('fpstorepickup')->__('Init'),
14
+ 'value' => 'Init',
15
+ ),
16
+ array(
17
+ 'label' => Mage::helper('fpstorepickup')->__('Waiting For Payment'),
18
+ 'value' => 'WaitingForPayment',
19
+ ),
20
+ array(
21
+ 'label' => Mage::helper('fpstorepickup')->__('Waiting For Payment Confirm'),
22
+ 'value' => 'WaitingForPaymentConfirm',
23
+ ),
24
+ array(
25
+ 'label' => Mage::helper('fpstorepickup')->__('Waiting For Payment Capture'),
26
+ 'value' => 'WaitingForPaymentCapture',
27
+ ),
28
+ array(
29
+ 'label' => Mage::helper('fpstorepickup')->__('Booked'),
30
+ 'value' => 'Booked',
31
+ ),
32
+ array(
33
+ 'label' => Mage::helper('fpstorepickup')->__('Arrived'),
34
+ 'value' => 'Arrived',
35
+ ),
36
+ array(
37
+ 'label' => Mage::helper('fpstorepickup')->__('Collected'),
38
+ 'value' => 'Collected',
39
+ ),
40
+ array(
41
+ 'label' => Mage::helper('fpstorepickup')->__('Canceled'),
42
+ 'value' => 'Canceled',
43
+ ),
44
+ array(
45
+ 'label' => Mage::helper('fpstorepickup')->__('Error'),
46
+ 'value' => 'Error',
47
+ ),
48
+
49
+ );
50
+ }
51
+
52
+ return $this->_options;
53
+ }
54
+
55
+ public function toOptionMap()
56
+ {
57
+ $result = array();
58
+ foreach ($this->toOptionArray() as $option)
59
+ $result[$option['value']] = $option['label'];
60
+ return $result;
61
+ }
62
+
63
+ }
app/code/community/FermoPoint/StorePickup/controllers/Adminhtml/RemoteController.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_Adminhtml_RemoteController extends Mage_Adminhtml_Controller_Action {
4
+
5
+ public function indexAction()
6
+ {
7
+ if ($this->getRequest()->getQuery('ajax')) {
8
+ $this->_forward('grid');
9
+ return;
10
+ }
11
+ $this->loadLayout();
12
+ $this->_addBreadcrumb($this->__('Orders'), $this->__('Orders'));
13
+ $this->_addBreadcrumb($this->__('All Orders'), $this->__('All Orders'));
14
+ $this->renderLayout();
15
+ }
16
+
17
+ public function gridAction()
18
+ {
19
+ $this->loadLayout();
20
+ $this->renderLayout();
21
+ }
22
+
23
+ public function exportCsvAction()
24
+ {
25
+ $fileName = 'orders.csv';
26
+ $content = $this->getLayout()->createBlock('fpstorepickup/adminhtml_remote_grid')
27
+ ->getCsvFile();
28
+
29
+ $this->_prepareDownloadResponse($fileName, $content);
30
+ }
31
+
32
+ public function exportXmlAction()
33
+ {
34
+ $fileName = 'orders.xml';
35
+ $content = $this->getLayout()->createBlock('fpstorepickup/adminhtml_remote_grid')
36
+ ->getExcelFile();
37
+
38
+ $this->_prepareDownloadResponse($fileName, $content);
39
+ }
40
+
41
+ }
app/code/community/FermoPoint/StorePickup/controllers/IndexController.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FermoPoint_StorePickup_IndexController extends Mage_Core_Controller_Front_Action
4
+ {
5
+
6
+ public function changemethodAction()
7
+ {
8
+ $flag = (bool) $this->getRequest()->getParam('flag');
9
+ Mage::helper('fpstorepickup')->setUseMethod($flag);
10
+ }
11
+
12
+ public function searchAction()
13
+ {
14
+ $this->getResponse()->setHeader('Content-type', 'application/json');
15
+
16
+ $response = new Varien_Object();
17
+ $response->setError(false);
18
+
19
+ try {
20
+ $request = Mage::getModel('fpstorepickup/api_searchData');
21
+ $request->loadFromPost($this->getRequest()->getPost());
22
+ $response->setLatitude($request->getLatitude());
23
+ $response->setLongitude($request->getLongitude());
24
+ $response->setPoints(Mage::getSingleton('fpstorepickup/points')->getPoints($request));
25
+ } catch (Mage_Core_Exception $e) {
26
+ $response->setError(true);
27
+ $response->setMessage($e->getMessage());
28
+ } catch (FermoPoint_StorePickup_Exception $e) {
29
+ $response->setError(true);
30
+ $response->setMessage($this->__('Error while communicating with FermoPoint. Please retry later.'));
31
+ } catch (Exception $e) {
32
+ Mage::logException($e);
33
+ $response->setError(true);
34
+ $response->setMessage($this->__('Unknown error. Please retry later.'));
35
+ }
36
+
37
+ $this->getResponse()->setBody($response->toJson());
38
+ }
39
+
40
+ }
app/code/community/FermoPoint/StorePickup/etc/adminhtml.xml ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <menu>
4
+ <sales translate="title" module="sales">
5
+ <children>
6
+ <fermopoint translate="title" module="fpstorepickup">
7
+ <title>Fermo!Point</title>
8
+ <children>
9
+ <orders translate="title" module="fpstorepickup">
10
+ <title>All Orders</title>
11
+ <action>fpstorepickupadmin/adminhtml_remote/index</action>
12
+ <sort_order>100</sort_order>
13
+ </orders>
14
+ </children>
15
+ <sort_order>1000</sort_order>
16
+ </fermopoint>
17
+ </children>
18
+ </sales>
19
+ </menu>
20
+ <acl>
21
+ <resources>
22
+ <admin>
23
+ <children>
24
+ <sales>
25
+ <children>
26
+ <fermopoint>
27
+ <title>Fermo!Point Management</title>
28
+ </fermopoint>
29
+ </children>
30
+ </sales>
31
+ </children>
32
+ </admin>
33
+ </resources>
34
+ </acl>
35
+ </config>
app/code/community/FermoPoint/StorePickup/etc/config.xml ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <FermoPoint_StorePickup>
5
+ <version>1.0.2</version>
6
+ </FermoPoint_StorePickup>
7
+ </modules>
8
+ <global>
9
+ <models>
10
+ <fpstorepickup>
11
+ <class>FermoPoint_StorePickup_Model</class>
12
+ <resourceModel>fpstorepickup_resource</resourceModel>
13
+ </fpstorepickup>
14
+ <fpstorepickup_resource>
15
+ <class>FermoPoint_StorePickup_Model_Resource</class>
16
+ <entities>
17
+ <point>
18
+ <table>fpsp_point</table>
19
+ </point>
20
+ <order_point>
21
+ <table>fpsp_order_point</table>
22
+ </order_point>
23
+ </entities>
24
+ </fpstorepickup_resource>
25
+ </models>
26
+ <blocks>
27
+ <fpstorepickup>
28
+ <class>FermoPoint_StorePickup_Block</class>
29
+ </fpstorepickup>
30
+ <checkout>
31
+ <rewrite>
32
+ <onepage_payment_methods>FermoPoint_StorePickup_Block_Checkout_Onepage_Payment_Methods</onepage_payment_methods>
33
+ <onepage_shipping_method_available>FermoPoint_StorePickup_Block_Checkout_Onepage_Shipping_Method_Available</onepage_shipping_method_available>
34
+ </rewrite>
35
+ </checkout>
36
+ </blocks>
37
+ <helpers>
38
+ <fpstorepickup>
39
+ <class>FermoPoint_StorePickup_Helper</class>
40
+ </fpstorepickup>
41
+ </helpers>
42
+ <resources>
43
+ <fpstorepickup_setup>
44
+ <setup>
45
+ <module>FermoPoint_StorePickup</module>
46
+ </setup>
47
+ <connection>
48
+ <use>core_setup</use>
49
+ </connection>
50
+ </fpstorepickup_setup>
51
+ <fpstorepickup_write>
52
+ <connection>
53
+ <use>core_write</use>
54
+ </connection>
55
+ </fpstorepickup_write>
56
+ <fpstorepickup_read>
57
+ <connection>
58
+ <use>core_read</use>
59
+ </connection>
60
+ </fpstorepickup_read>
61
+ </resources>
62
+ <events>
63
+ <sales_order_invoice_pay>
64
+ <observers>
65
+ <fermopoint_storepickup_observer>
66
+ <type>singleton</type>
67
+ <class>fpstorepickup/observer</class>
68
+ <method>onOrderInvoicePay</method>
69
+ </fermopoint_storepickup_observer>
70
+ </observers>
71
+ </sales_order_invoice_pay>
72
+ <order_cancel_after>
73
+ <observers>
74
+ <fermopoint_storepickup_observer>
75
+ <type>singleton</type>
76
+ <class>fpstorepickup/observer</class>
77
+ <method>onOrderCancel</method>
78
+ </fermopoint_storepickup_observer>
79
+ </observers>
80
+ </order_cancel_after>
81
+ </events>
82
+ </global>
83
+ <frontend>
84
+ <translate>
85
+ <modules>
86
+ <FermoPoint_StorePickup>
87
+ <files>
88
+ <default>FermoPoint_StorePickup.csv</default>
89
+ </files>
90
+ </FermoPoint_StorePickup>
91
+ </modules>
92
+ </translate>
93
+ <routers>
94
+ <fpstorepickup>
95
+ <use>standard</use>
96
+ <args>
97
+ <module>FermoPoint_StorePickup</module>
98
+ <frontName>fpstorepickup</frontName>
99
+ </args>
100
+ </fpstorepickup>
101
+ </routers>
102
+ <layout>
103
+ <updates>
104
+ <fpstorepickup>
105
+ <file>fpstorepickup.xml</file>
106
+ </fpstorepickup>
107
+ </updates>
108
+ </layout>
109
+ <events>
110
+ <core_block_abstract_to_html_after>
111
+ <observers>
112
+ <fermopoint_storepickup_observer>
113
+ <type>singleton</type>
114
+ <class>fpstorepickup/observer</class>
115
+ <method>onBlockToHtmlAfter</method>
116
+ </fermopoint_storepickup_observer>
117
+ </observers>
118
+ </core_block_abstract_to_html_after>
119
+ <checkout_type_onepage_save_order_after>
120
+ <observers>
121
+ <fermopoint_storepickup_observer>
122
+ <type>singleton</type>
123
+ <class>fpstorepickup/observer</class>
124
+ <method>onSaveOrderAfter</method>
125
+ </fermopoint_storepickup_observer>
126
+ </observers>
127
+ </checkout_type_onepage_save_order_after>
128
+ <checkout_controller_onepage_save_shipping_method>
129
+ <observers>
130
+ <fermopoint_storepickup_observer>
131
+ <type>singleton</type>
132
+ <class>fpstorepickup/observer</class>
133
+ <method>onSaveShippingMethodAfter</method>
134
+ </fermopoint_storepickup_observer>
135
+ </observers>
136
+ </checkout_controller_onepage_save_shipping_method>
137
+ <controller_action_predispatch_checkout_onepage_saveShippingMethod>
138
+ <observers>
139
+ <fermopoint_storepickup_observer>
140
+ <class>fpstorepickup/observer</class>
141
+ <method>onSaveShippingMethodBefore</method>
142
+ </fermopoint_storepickup_observer>
143
+ </observers>
144
+ </controller_action_predispatch_checkout_onepage_saveShippingMethod>
145
+ </events>
146
+ </frontend>
147
+ <admin>
148
+ <routers>
149
+ <fpstorepickup>
150
+ <use>admin</use>
151
+ <args>
152
+ <module>FermoPoint_StorePickup</module>
153
+ <frontName>fpstorepickupadmin</frontName>
154
+ </args>
155
+ </fpstorepickup>
156
+ </routers>
157
+ </admin>
158
+ <adminhtml>
159
+ <layout>
160
+ <updates>
161
+ <fpstorepickup>
162
+ <file>fpstorepickup.xml</file>
163
+ </fpstorepickup>
164
+ </updates>
165
+ </layout>
166
+ <translate>
167
+ <modules>
168
+ <FermoPoint_StorePickup>
169
+ <files>
170
+ <default>FermoPoint_StorePickup.csv</default>
171
+ </files>
172
+ </FermoPoint_StorePickup>
173
+ </modules>
174
+ </translate>
175
+ <layout>
176
+ <updates>
177
+ <fpstorepickup>
178
+ <file>fpstorepickup.xml</file>
179
+ </fpstorepickup>
180
+ </updates>
181
+ </layout>
182
+ </adminhtml>
183
+ <default>
184
+ <carriers>
185
+ <fpstorepickup>
186
+ <accept>0</accept>
187
+ <active>0</active>
188
+ <model>fpstorepickup/carrier_storepickup</model>
189
+ <title>Corriere con Consegna presso Fermo!Point</title>
190
+ <sandbox>1</sandbox>
191
+ <debug>1</debug>
192
+ <cost>0</cost>
193
+ <maximum_subtotal>500</maximum_subtotal>
194
+ <maximum_weight>15</maximum_weight>
195
+ <tos_url>http://www.fermopoint.it/contratto-utenti/</tos_url>
196
+ <allowspecific_payment>1</allowspecific_payment>
197
+ </fpstorepickup>
198
+ </carriers>
199
+ </default>
200
+ </config>
app/code/community/FermoPoint/StorePickup/etc/jstranslator.xml ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <jstranslator>
3
+ <fermopoint-agreement translate="message" module="fpstorepickup">
4
+ <message>You should accept FermoPoint terms and conditions</message>
5
+ </fermopoint-agreement>
6
+ <fermopoint-select-point translate="message" module="fpstorepickup">
7
+ <message>You should select one of available pick-up points to continue</message>
8
+ </fermopoint-select-point>
9
+ <fermopoint-error translate="message" module="fpstorepickup">
10
+ <message>Error</message>
11
+ </fermopoint-error>
12
+ <fermopoint-use-point translate="message" module="fpstorepickup">
13
+ <message>Select this pick-up point</message>
14
+ </fermopoint-use-point>
15
+ <fermopoint-distance translate="message" module="fpstorepickup">
16
+ <message>Distance</message>
17
+ </fermopoint-distance>
18
+ <fermopoint-category translate="message" module="fpstorepickup">
19
+ <message>Category</message>
20
+ </fermopoint-category>
21
+ <fermopoint-contact translate="message" module="fpstorepickup">
22
+ <message>Contact Person</message>
23
+ </fermopoint-contact>
24
+ <fermopoint-hours translate="message" module="fpstorepickup">
25
+ <message>Hours</message>
26
+ </fermopoint-hours>
27
+ </jstranslator>
app/code/community/FermoPoint/StorePickup/etc/system.xml ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <sections>
4
+ <carriers>
5
+ <groups>
6
+ <fpstorepickup translate="label" module="fpstorepickup">
7
+ <label>FermoPoint</label>
8
+ <frontend_type>text</frontend_type>
9
+ <sort_order>12</sort_order>
10
+ <show_in_default>1</show_in_default>
11
+ <show_in_website>1</show_in_website>
12
+ <show_in_store>1</show_in_store>
13
+ <fields>
14
+ <configuration_manual translate="label">
15
+ <label></label>
16
+ <frontend_model>fpstorepickup/adminhtml_configuration_manual</frontend_model>
17
+ <backend_model>adminhtml/system_config_backend_serialized_array</backend_model>
18
+ <sort_order>0</sort_order>
19
+ <show_in_default>1</show_in_default>
20
+ <show_in_website>1</show_in_website>
21
+ <show_in_store>0</show_in_store>
22
+ </configuration_manual>
23
+ <accept translate="label comment">
24
+ <label>Accept TOS</label>
25
+ <comment><![CDATA[I accept <a href="https://www.fermopoint.it/termini-merchant/" target="_blank">FermoPoint Terms of Service</a>]]></comment>
26
+ <frontend_type>select</frontend_type>
27
+ <source_model>adminhtml/system_config_source_yesno</source_model>
28
+ <sort_order>1</sort_order>
29
+ <show_in_default>1</show_in_default>
30
+ <show_in_website>1</show_in_website>
31
+ <show_in_store>0</show_in_store>
32
+ </accept>
33
+ <active translate="label">
34
+ <label>Enabled</label>
35
+ <frontend_type>select</frontend_type>
36
+ <source_model>adminhtml/system_config_source_yesno</source_model>
37
+ <sort_order>10</sort_order>
38
+ <show_in_default>1</show_in_default>
39
+ <show_in_website>1</show_in_website>
40
+ <show_in_store>0</show_in_store>
41
+ <depends><accept>1</accept></depends>
42
+ </active>
43
+ <title translate="label">
44
+ <label>Title</label>
45
+ <frontend_type>text</frontend_type>
46
+ <sort_order>11</sort_order>
47
+ <show_in_default>1</show_in_default>
48
+ <show_in_website>1</show_in_website>
49
+ <show_in_store>0</show_in_store>
50
+ <depends><accept>1</accept></depends>
51
+ </title>
52
+
53
+ <client_id translate="label">
54
+ <label>Client ID</label>
55
+ <frontend_type>text</frontend_type>
56
+ <sort_order>20</sort_order>
57
+ <show_in_default>1</show_in_default>
58
+ <show_in_website>1</show_in_website>
59
+ <show_in_store>0</show_in_store>
60
+ <depends><accept>1</accept></depends>
61
+ </client_id>
62
+ <client_secret translate="label">
63
+ <label>Client Secret</label>
64
+ <frontend_type>text</frontend_type>
65
+ <sort_order>21</sort_order>
66
+ <show_in_default>1</show_in_default>
67
+ <show_in_website>1</show_in_website>
68
+ <show_in_store>0</show_in_store>
69
+ <depends><accept>1</accept></depends>
70
+ </client_secret>
71
+ <sandbox translate="label">
72
+ <label>Sandbox</label>
73
+ <frontend_type>select</frontend_type>
74
+ <source_model>adminhtml/system_config_source_yesno</source_model>
75
+ <sort_order>22</sort_order>
76
+ <show_in_default>1</show_in_default>
77
+ <show_in_website>1</show_in_website>
78
+ <show_in_store>0</show_in_store>
79
+ <depends><accept>1</accept></depends>
80
+ </sandbox>
81
+ <debug translate="label">
82
+ <label>Debug</label>
83
+ <frontend_type>select</frontend_type>
84
+ <source_model>adminhtml/system_config_source_yesno</source_model>
85
+ <sort_order>23</sort_order>
86
+ <show_in_default>1</show_in_default>
87
+ <show_in_website>1</show_in_website>
88
+ <show_in_store>0</show_in_store>
89
+ <depends><accept>1</accept></depends>
90
+ </debug>
91
+
92
+ <cost translate="label">
93
+ <label>Cost</label>
94
+ <frontend_type>text</frontend_type>
95
+ <sort_order>30</sort_order>
96
+ <validate>validate-number</validate>
97
+ <show_in_default>1</show_in_default>
98
+ <show_in_website>1</show_in_website>
99
+ <show_in_store>0</show_in_store>
100
+ <depends><accept>1</accept></depends>
101
+ </cost>
102
+ <maximum_subtotal translate="label">
103
+ <label>Maximum Subtotal</label>
104
+ <frontend_type>text</frontend_type>
105
+ <sort_order>31</sort_order>
106
+ <validate>validate-number</validate>
107
+ <show_in_default>1</show_in_default>
108
+ <show_in_website>1</show_in_website>
109
+ <show_in_store>0</show_in_store>
110
+ <depends><accept>1</accept></depends>
111
+ </maximum_subtotal>
112
+ <maximum_weight translate="label">
113
+ <label>Maximum Weight</label>
114
+ <frontend_type>text</frontend_type>
115
+ <sort_order>32</sort_order>
116
+ <validate>validate-number</validate>
117
+ <show_in_default>1</show_in_default>
118
+ <show_in_website>1</show_in_website>
119
+ <show_in_store>0</show_in_store>
120
+ <depends><accept>1</accept></depends>
121
+ </maximum_weight>
122
+
123
+ <tos_url translate="label comment">
124
+ <label>Terms and Conditions Url</label>
125
+ <frontend_type>text</frontend_type>
126
+ <!--frontend_model>fpstorepickup/adminhtml_configuration_disabled</frontend_model-->
127
+ <sort_order>40</sort_order>
128
+ <show_in_default>1</show_in_default>
129
+ <show_in_website>1</show_in_website>
130
+ <show_in_store>0</show_in_store>
131
+ <depends><accept>1</accept></depends>
132
+ <comment>Optional, use to show some TOS in iframe</comment>
133
+ </tos_url>
134
+ <gmaps_key translate="label comment">
135
+ <label>Google Maps API Key</label>
136
+ <frontend_type>text</frontend_type>
137
+ <sort_order>40</sort_order>
138
+ <show_in_default>1</show_in_default>
139
+ <show_in_website>1</show_in_website>
140
+ <show_in_store>0</show_in_store>
141
+ <comment>Optional, use for high load only</comment>
142
+ <depends><accept>1</accept></depends>
143
+ </gmaps_key>
144
+
145
+ <allowspecific_payment translate="label">
146
+ <label>Applicable payments</label>
147
+ <frontend_type>select</frontend_type>
148
+ <sort_order>100</sort_order>
149
+ <source_model>fpstorepickup/source_selectorpayment</source_model>
150
+ <show_in_default>1</show_in_default>
151
+ <show_in_website>0</show_in_website>
152
+ <show_in_store>0</show_in_store>
153
+ <depends><accept>1</accept></depends>
154
+ </allowspecific_payment>
155
+
156
+ <specificpayment translate="label comment">
157
+ <label>Specific payments</label>
158
+ <frontend_type>multiselect</frontend_type>
159
+ <sort_order>110</sort_order>
160
+ <source_model>fpstorepickup/source_payment</source_model>
161
+ <show_in_default>1</show_in_default>
162
+ <show_in_website>1</show_in_website>
163
+ <show_in_store>0</show_in_store>
164
+ <depends><accept>1</accept><allowspecific_payment>1</allowspecific_payment></depends>
165
+ <comment>Warning! Do not select "Cash on Delivery" methods, as we do not support them</comment>
166
+ </specificpayment>
167
+
168
+ </fields>
169
+ </fpstorepickup>
170
+ </groups>
171
+ </carriers>
172
+ </sections>
173
+ </config>
app/code/community/FermoPoint/StorePickup/sql/fpstorepickup_setup/mysql4-install-1.0.0.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+
5
+ $installer->startSetup();
6
+
7
+ if (!$installer->tableExists($installer->getTable('fpstorepickup/point'))) {
8
+ $installer->run("
9
+
10
+ CREATE TABLE IF NOT EXISTS `{$installer->getTable('fpstorepickup/point')}` (
11
+ `point_id` int(10) unsigned NOT NULL COMMENT 'Point ID',
12
+ `name` text NOT NULL COMMENT 'Point Name',
13
+ `address` text NOT NULL COMMENT 'Point Address',
14
+ `point_data` text NOT NULL COMMENT 'Serialized Point Data',
15
+ PRIMARY KEY (`point_id`)
16
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Fermopoint Storepickup Point';
17
+
18
+ ");
19
+ }
20
+
21
+ if (!$installer->tableExists($installer->getTable('fpstorepickup/order_point'))) {
22
+ $installer->run("
23
+
24
+ CREATE TABLE IF NOT EXISTS `{$installer->getTable('fpstorepickup/order_point')}` (
25
+ `order_point_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Order Point ID',
26
+ `order_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Order ID',
27
+ `point_id` int(10) unsigned NULL DEFAULT NULL COMMENT 'Point ID',
28
+ `ticket_id` varchar(255) NOT NULL COMMENT 'Ticket ID',
29
+ `is_approved` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Is Approved Flag',
30
+ `is_cancelled` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Is Cancelled Flag',
31
+ `account_type` varchar(255) NOT NULL COMMENT 'Account Type',
32
+ `nickname` varchar(255) NOT NULL COMMENT 'Nickname',
33
+ `email` varchar(255) NOT NULL COMMENT 'Email',
34
+ `phone_number` varchar(255) NOT NULL COMMENT 'Phone Number',
35
+ `point_name` text NOT NULL COMMENT 'Point Name',
36
+ `point_address` text NOT NULL COMMENT 'Point Address',
37
+ `point_data` text NOT NULL COMMENT 'Serialized Point Data',
38
+ PRIMARY KEY (`order_point_id`),
39
+ KEY `IDX_FPSP_ORDER_POINT_ORDER_ID` (`order_id`),
40
+ KEY `IDX_FPSP_ORDER_POINT_POINT_ID` (`point_id`),
41
+ CONSTRAINT `FK_FPSP_ORDER_POINT_POINT_ID_FPSP_POINT_POINT_ID` FOREIGN KEY (`point_id`) REFERENCES {$installer->getTable('fpstorepickup/point')} (`point_id`) ON DELETE SET NULL ON UPDATE CASCADE,
42
+ CONSTRAINT `FK_FPSP_ORDER_POINT_ORDER_ID_SALES_FLAT_ORDER_ENTITY_ID` FOREIGN KEY (`order_id`) REFERENCES {$installer->getTable('sales/order')} (`entity_id`) ON DELETE CASCADE ON UPDATE CASCADE
43
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Fermopoint Storepickup Order to Point';
44
+
45
+ ");
46
+
47
+ }
48
+
49
+ $installer->endSetup();
app/code/community/FermoPoint/StorePickup/sql/fpstorepickup_setup/mysql4-upgrade-1.0.0-1.0.1.php ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+
5
+ $installer->startSetup();
6
+
7
+ $collection = Mage::getModel('directory/region')->getResourceCollection()
8
+ ->addCountryFilter('ITA')
9
+ ->load()
10
+ ;
11
+
12
+ if ( ! $collection->getSize())
13
+ {
14
+ $data = array(
15
+ array('IT', 'AG', 'Agrigento', 'Agrigento'),
16
+ array('IT', 'AL', 'Alessandria', 'Alessandria'),
17
+ array('IT', 'AN', 'Ancona', 'Ancona'),
18
+ array('IT', 'AO', 'Aosta', 'Aosta'),
19
+ array('IT', 'AR', 'Arezzo', 'Arezzo'),
20
+ array('IT', 'AP', 'Ascoli Piceno', 'Ascoli Piceno'),
21
+ array('IT', 'AT', 'Asti', 'Asti'),
22
+ array('IT', 'AV', 'Avellino', 'Avellino'),
23
+ array('IT', 'BA', 'Bari', 'Bari'),
24
+ array('IT', 'BT', 'Barletta-Andria-Trani', 'Barletta-Andria-Trani'),
25
+ array('IT', 'BL', 'Belluno', 'Belluno'),
26
+ array('IT', 'BN', 'Benevento', 'Benevento'),
27
+ array('IT', 'BG', 'Bergamo', 'Bergamo'),
28
+ array('IT', 'BI', 'Biella', 'Biella'),
29
+ array('IT', 'BO', 'Bologna', 'Bologna'),
30
+ array('IT', 'BZ', 'Bolzano', 'Bolzano'),
31
+ array('IT', 'BS', 'Brescia', 'Brescia'),
32
+ array('IT', 'BR', 'Brindisi', 'Brindisi'),
33
+ array('IT', 'CA', 'Cagliari', 'Cagliari'),
34
+ array('IT', 'CL', 'Caltanissetta', 'Caltanissetta'),
35
+ array('IT', 'CB', 'Campobasso', 'Campobasso'),
36
+ array('IT', 'CI', 'Carbonia-Iglesias', 'Carbonia-Iglesias'),
37
+ array('IT', 'CE', 'Caserta', 'Caserta'),
38
+ array('IT', 'CT', 'Catania', 'Catania'),
39
+ array('IT', 'CZ', 'Catanzaro', 'Catanzaro'),
40
+ array('IT', 'CH', 'Chieti', 'Chieti'),
41
+ array('IT', 'CO', 'Como', 'Como'),
42
+ array('IT', 'CS', 'Cosenza', 'Cosenza'),
43
+ array('IT', 'CR', 'Cremona', 'Cremona'),
44
+ array('IT', 'KR', 'Crotone', 'Crotone'),
45
+ array('IT', 'CN', 'Cuneo', 'Cuneo'),
46
+ array('IT', 'EN', 'Enna', 'Enna'),
47
+ array('IT', 'FM', 'Fermo', 'Fermo'),
48
+ array('IT', 'FE', 'Ferrara', 'Ferrara'),
49
+ array('IT', 'FI', 'Florence', 'Firenze'),
50
+ array('IT', 'FG', 'Foggia', 'Foggia'),
51
+ array('IT', 'FC', 'Forlì-Cesena', 'Forlì-Cesena'),
52
+ array('IT', 'FR', 'Frosinone', 'Frosinone'),
53
+ array('IT', 'GE', 'Genoa', 'Genova'),
54
+ array('IT', 'GO', 'Gorizia', 'Gorizia'),
55
+ array('IT', 'GR', 'Grosseto', 'Grosseto'),
56
+ array('IT', 'IM', 'Imperia', 'Imperia'),
57
+ array('IT', 'IS', 'Isernia', 'Isernia'),
58
+ array('IT', 'SP', 'La Spezia', 'La Spezia'),
59
+ array('IT', 'AQ', 'L\'Aquila', 'L\'Aquila'),
60
+ array('IT', 'LT', 'Latina', 'Latina'),
61
+ array('IT', 'LE', 'Lecce', 'Lecce'),
62
+ array('IT', 'LC', 'Lecco', 'Lecco'),
63
+ array('IT', 'LI', 'Livorno', 'Livorno'),
64
+ array('IT', 'LO', 'Lodi', 'Lodi'),
65
+ array('IT', 'LU', 'Lucca', 'Lucca'),
66
+ array('IT', 'MC', 'Macerata', 'Macerata'),
67
+ array('IT', 'MN', 'Mantua', 'Mantova'),
68
+ array('IT', 'MS', 'Massa and Carrara', 'Massa e Carrara'),
69
+ array('IT', 'MT', 'Matera', 'Matera'),
70
+ array('IT', 'VS', 'Medio Campidano', 'Medio Campidano'),
71
+ array('IT', 'ME', 'Messina', 'Messina'),
72
+ array('IT', 'MI', 'Milan', 'Milano'),
73
+ array('IT', 'MO', 'Modena', 'Modena'),
74
+ array('IT', 'MB', 'Monza and Brianza', 'Monza e Brianza'),
75
+ array('IT', 'NA', 'Naples', 'Napoli'),
76
+ array('IT', 'NO', 'Novara', 'Novara'),
77
+ array('IT', 'NU', 'Nuoro', 'Nuoro'),
78
+ array('IT', 'OG', 'Ogliastra', 'Ogliastra'),
79
+ array('IT', 'OT', 'Olbia-Tempio', 'Olbia-Tempio'),
80
+ array('IT', 'OR', 'Oristano', 'Oristano'),
81
+ array('IT', 'PD', 'Padua', 'Padova'),
82
+ array('IT', 'PA', 'Palermo', 'Palermo'),
83
+ array('IT', 'PR', 'Parma', 'Parma'),
84
+ array('IT', 'PV', 'Pavia', 'Pavia'),
85
+ array('IT', 'PG', 'Perugia', 'Perugia'),
86
+ array('IT', 'PU', 'Pesaro and Urbino', 'Pesaro e Urbino'),
87
+ array('IT', 'PE', 'Pescara', 'Pescara'),
88
+ array('IT', 'PC', 'Piacenza', 'Piacenza'),
89
+ array('IT', 'PI', 'Pisa', 'Pisa'),
90
+ array('IT', 'PT', 'Pistoia', 'Pistoia'),
91
+ array('IT', 'PN', 'Pordenone', 'Pordenone'),
92
+ array('IT', 'PZ', 'Potenza', 'Potenza'),
93
+ array('IT', 'PO', 'Prato', 'Prato'),
94
+ array('IT', 'RG', 'Ragusa', 'Ragusa'),
95
+ array('IT', 'RA', 'Ravenna', 'Ravenna'),
96
+ array('IT', 'RC', 'Reggio Calabria', 'Reggio Calabria'),
97
+ array('IT', 'RE', 'Reggio Emilia', 'Reggio Emilia'),
98
+ array('IT', 'RI', 'Rieti', 'Rieti'),
99
+ array('IT', 'RN', 'Rimini', 'Rimini'),
100
+ array('IT', 'RM', 'Rome', 'Roma'),
101
+ array('IT', 'RO', 'Rovigo', 'Rovigo'),
102
+ array('IT', 'SA', 'Salerno', 'Salerno'),
103
+ array('IT', 'SS', 'Sassari', 'Sassari'),
104
+ array('IT', 'SV', 'Savona', 'Savona'),
105
+ array('IT', 'SI', 'Siena', 'Siena'),
106
+ array('IT', 'SO', 'Sondrio', 'Sondrio'),
107
+ array('IT', 'SR', 'Syracuse', 'Siracusa'),
108
+ array('IT', 'TA', 'Taranto', 'Taranto'),
109
+ array('IT', 'TE', 'Teramo', 'Teramo'),
110
+ array('IT', 'TR', 'Terni', 'Terni'),
111
+ array('IT', 'TO', 'Turin', 'Torino'),
112
+ array('IT', 'TP', 'Trapani', 'Trapani'),
113
+ array('IT', 'TN', 'Trento', 'Trento'),
114
+ array('IT', 'TV', 'Treviso', 'Treviso'),
115
+ array('IT', 'TS', 'Trieste', 'Trieste'),
116
+ array('IT', 'UD', 'Udine', 'Udine'),
117
+ array('IT', 'VA', 'Varese', 'Varese'),
118
+ array('IT', 'VE', 'Venice', 'Venezia'),
119
+ array('IT', 'VB', 'Verbano-Cusio-Ossola', 'Verbano-Cusio-Ossola'),
120
+ array('IT', 'VC', 'Vercelli', 'Vercelli'),
121
+ array('IT', 'VR', 'Verona', 'Verona'),
122
+ array('IT', 'VV', 'Vibo Valentia', 'Vibo Valentia'),
123
+ array('IT', 'VI', 'Vicenza', 'Vicenza'),
124
+ array('IT', 'VT', 'Viterbo', 'Viterbo'),
125
+ );
126
+
127
+ foreach ($data as $row)
128
+ {
129
+ $bind = array(
130
+ 'country_id' => $row[0],
131
+ 'code' => $row[1],
132
+ 'default_name' => $row[2],
133
+ );
134
+ $installer->getConnection()->insert($installer->getTable('directory/country_region'), $bind);
135
+ $regionId = $installer->getConnection()->lastInsertId($installer->getTable('directory/country_region'));
136
+
137
+ $bind = array(
138
+ 'locale' => 'it_IT',
139
+ 'region_id' => $regionId,
140
+ 'name' => $row[3],
141
+ );
142
+ $installer->getConnection()->insert($installer->getTable('directory/country_region_name'), $bind);
143
+ }
144
+ }
145
+
146
+ $installer->endSetup();
app/code/community/FermoPoint/StorePickup/sql/fpstorepickup_setup/mysql4-upgrade-1.0.1-1.0.2.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+
5
+ $installer->startSetup();
6
+
7
+ $installer->setConfigData('general/region/state_required', 'AT,CA,EE,FI,FR,DE,IT,LV,LT,RO,ES,US,CH');
8
+
9
+ $installer->endSetup();
app/design/adminhtml/default/default/layout/fpstorepickup.xml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout>
3
+ <fpstorepickup_adminhtml_remote_index>
4
+ <referense name="head">
5
+ <action method="setTitle" translate="title"><title>All Orders</title></action>
6
+ </referense>
7
+ <reference name="menu">
8
+ <action method="setActive"><menupath>sales/fermopoint/remote</menupath></action>
9
+ </reference>
10
+ <reference name="content">
11
+ <block type="core/text_list" name="remote">
12
+ <block type="fpstorepickup/adminhtml_stats" name="admin.fermopoint.stats" template="fpstorepickup/stats.phtml" />
13
+ <block type="fpstorepickup/adminhtml_remote" name="admin.fermopoint.remote"/>
14
+ </block>
15
+ </reference>
16
+ </fpstorepickup_adminhtml_remote_index>
17
+ <fpstorepickup_adminhtml_remote_grid>
18
+ <block type="core/text_list" name="root" output="toHtml">
19
+ <block type="fpstorepickup/adminhtml_remote_grid" name="admin.fermopoint.remote.grid"/>
20
+ </block>
21
+ </fpstorepickup_adminhtml_remote_grid>
22
+ </layout>
app/design/adminhtml/default/default/template/fpstorepickup/stats.phtml ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php $credits = $this->getCredits() ?>
2
+ <?php if ($credits !== false) : ?>
3
+ <div class="box-left">
4
+ <div class="entry-edit">
5
+ <div class="entry-edit-head"><h4><?php echo $this->__('Credits') ?></h4></div>
6
+ <fieldset class="a-center bold">
7
+ <span class="nowrap" style="font-size:18px;"><span class="price"><?php echo $credits ?></span><span style="font-size:14px; color:#686868;"></span></span>
8
+ </fieldset>
9
+ </div>
10
+ </div>
11
+ <div class="box-right">
12
+ <div class="entry-edit">
13
+ <div class="entry-edit-head"><h4><?php echo $this->__('Statistics') ?></h4></div>
14
+ <fieldset class="np">
15
+ <div class="grid np">
16
+ <table cellspacing="0" style="border:0;" id="ordersCountTable">
17
+ <colgroup>
18
+ <col>
19
+ <col width="100">
20
+ <col width="100">
21
+ </colgroup>
22
+ <thead>
23
+ <tr class="headings">
24
+ <th class=" no-link"><span class="nobr"><?php echo $this->__('Status') ?></span></th>
25
+ <th class=" no-link last"><span class="nobr"><?php echo $this->__('Count') ?></span></th>
26
+ </tr>
27
+ </thead>
28
+ <tbody>
29
+ <?php foreach ($this->getOrders() as $idx => $row) : ?>
30
+ <tr class="<?php echo $idx %2 ? 'even' : 'odd' ?>">
31
+ <td class=" "><?php echo $this->__($row['state']) ?></td>
32
+ <td class="a-right a-right last"><?php echo $this->__($row['count']) ?></td>
33
+ </tr>
34
+ <?php endforeach ?>
35
+ </tbody>
36
+ </table>
37
+ </div>
38
+ </fieldset>
39
+ </div>
40
+ </div>
41
+ <div class="clear"></div>
42
+ <?php else : ?>
43
+ <div id="messages"><ul class="messages"><li class="error-msg"><ul><li><span><?php echo $this->__('Service is not available at the moment, please try again later') ?></span></li></ul></li></ul></div>
44
+ <?php endif ?>
app/design/frontend/base/default/layout/fpstorepickup.xml ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout version="0.1.0">
3
+
4
+ <checkout_onepage_index>
5
+ <reference name="head">
6
+ <action method="addJs"><script>fermopoint/markerclusterer.js</script></action>
7
+ <action method="addJs"><script>fermopoint/storepickup.js</script></action>
8
+ <action method="addItem"><type>skin_css</type><name>fermopoint/css/storepickup.css</name></action>
9
+ </reference>
10
+ <!--reference name="checkout.onepage.billing">
11
+ <action method="setTemplate"><template>fpstorepickup/checkout/onepage/billing.phtml</template></action>
12
+ </reference-->
13
+ </checkout_onepage_index>
14
+
15
+ </layout>
app/design/frontend/base/default/template/fpstorepickup/checkout/onepage/billing/js.phtml ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script type="text/javascript">
2
+ //<![CDATA[
3
+ var fpStorePickup = new FermopointStorePickup(
4
+ '<?php echo Mage::helper('fpstorepickup')->getChangeMethodUrl(); ?>',
5
+ '<?php echo Mage::helper('fpstorepickup')->getSearchUrl(); ?>',
6
+ '<?php echo Mage::helper('fpstorepickup')->getMediaUrl(); ?>'
7
+ );
8
+
9
+ Event.observe('billing:use_for_shipping_point', 'click', function(event){
10
+ fpStorePickup.setUseStorePickup(true);
11
+ });
12
+
13
+ Event.observe('billing:use_for_shipping_yes', 'click', function(event){
14
+ fpStorePickup.setUseStorePickup(false);
15
+ });
16
+
17
+ Event.observe('billing:use_for_shipping_no', 'click', function(event){
18
+ fpStorePickup.setUseStorePickup(false);
19
+ });
20
+ //]]>
21
+ </script>
app/design/frontend/base/default/template/fpstorepickup/checkout/onepage/billing/radio.phtml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <li class="control">
2
+ <input type="radio" name="billing[use_for_shipping]" id="billing:use_for_shipping_point" value="1"<?php if (Mage::helper('fpstorepickup')->getUseMethod()) : ?> checked="checked" <?php endif ?> onclick="$('shipping:same_as_billing').checked = true;" class="radio" />
3
+ <label for="billing:use_for_shipping_point"><?php echo $this->__('Fermo!Point Store Pickup') ?></label>
4
+ </li>
5
+
app/design/frontend/base/default/template/fpstorepickup/map.phtml ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php $tosUrl = $this->getTosUrl() ?>
2
+ <?php $mapId = uniqid() ?>
3
+ <div class="fermopoint">
4
+ <div id="fermopoint_map">
5
+ <h3><?php echo $this->__('Search for pick-up points') ?></h3>
6
+ <p><?php echo $this->__('You are viewing the collection points closest to the address below. If you want, you can enter a different address.') ?></p>
7
+ <div class="address">
8
+ <div class="fermopoint-address">
9
+ <input type="text" id="fermopoint_search_address" value="<?php echo $this->htmlEscape($this->getBillingAddress()) ?>" size="50" />
10
+ <label for="fermopoint_search_radius"><?php echo $this->__('Radius') ?></label>
11
+ <select id="fermopoint_search_radius">
12
+ <option value="1">1 km</option>
13
+ <option value="5">5 km</option>
14
+ <option value="10">10 km</option>
15
+ <option value="20" selected="selected">20 km</option>
16
+ <option value="30">30 km</option>
17
+ <option value="50">50 km</option>
18
+ <option value="100">100 km</option>
19
+ </select>
20
+ </div>
21
+ <div class="fermopoint-search">
22
+ <button id="fermopoint_search" class="button"><span><span><?php echo $this->__('Search') ?></span></span></button>
23
+ <img src="<?php echo $this->getSkinUrl('fermopoint/images/opc-ajax-loader.gif') ?>" alt="<?php echo $this->__('Loading') ?>" />
24
+ </div>
25
+ </div>
26
+ <div id="fermopoint_google_map<?php echo $mapId ?>" class="fermopoint_google_map"></div>
27
+ </div>
28
+ <div id="fermopoint_point" style="display: none;">
29
+ <input type="hidden" name="fermopoint_point_id" id="fermopoint_point_id" value="0" />
30
+ <h3><?php echo $this->__('Selected pick-up point') ?></h3>
31
+ <h5 id="fermopoint_point_name"></h5>
32
+ <p id="fermopoint_point_address"></p>
33
+ <ul class="form-list">
34
+ <li class="control">
35
+ <input type="radio" name="fermopoint_account" id="fermopoint_account:new" value="new" title="<?php echo $this->__('I did not use Fermo!Point before') ?>" onclick="$$('.fermopoint-new-account').each(function (el) {el.show()});$$('.fermopoint-existing-account').each(function (el) {el.hide()});" checked="checked" class="radio" /><label for="fermopoint_account:new"><?php echo $this->__('I did not use Fermo!Point before') ?></label></li>
36
+ </li>
37
+ <li class="control">
38
+ <input type="radio" name="fermopoint_account" id="fermopoint_account:existing" value="existing" title="<?php echo $this->__('I have used Fermo!Point in the past') ?>" onclick="$$('.fermopoint-new-account').each(function (el) {el.hide()});$$('.fermopoint-existing-account').each(function (el) {el.show()});" class="radio" /><label for="fermopoint_account:existing"><?php echo $this->__('I have used Fermo!Point in the past') ?></label></li>
39
+ </li>
40
+ <li class="fields">
41
+ <div class="field">
42
+ <label for="fermopoint_nickname" class="required"><em>*</em><?php echo $this->__('Nickname') ?></label>
43
+ <label for="fermopoint_nickname" class="nick info fermopoint-existing-account" style="display:none;">(<?php echo $this->__('Please enter your Nickname. The nickname will be required at the time of withdrawal of the package') ?>)</label>
44
+ <label for="fermopoint_nickname" class="nick info fermopoint-new-account">(<?php echo $this->__('Choose and enter your nickname. The nickname will be required at the time of withdrawal of the package') ?>)</label>
45
+ <div class="input-box">
46
+ <input type="text" name="fermopoint_nickname" id="fermopoint_nickname" title="<?php echo $this->__('Nickname') ?>" class="input-text required-entry" />
47
+ </div>
48
+ </div>
49
+ <div class="field">
50
+ <label for="fermopoint_phone"><?php echo $this->__('Mobile phone') ?></label>
51
+ <label for="fermopoint_phone" class="phone info">(<?php echo $this->__('Enter your cell phone number for delivery') ?>)</label>
52
+ <div class="input-box">
53
+ <input type="text" name="fermopoint_phone" id="fermopoint_phone" title="<?php echo $this->__('Mobile phone') ?>" value="<?php echo $this->escapeHtml($this->getUserPhone()) ?>" class="input-text" />
54
+ </div>
55
+ </div>
56
+ </li>
57
+ </ul>
58
+ <p class="fermopoint-new-account">
59
+ <?php echo $this->__('The billing information will be disclosed to Fermo!Point to register the new account and the user will be used in the future on any other site that integrates eCommerce service Fermo!Point') ?>
60
+ </p>
61
+ <p class="fermopoint-new-account">
62
+ <?php echo $this->__('To the email address specified in the billing information <b>%s</ b> will receive an email with your login credentials to the portal Fermo!Point', $this->htmlEscape($this->getUserEmail())) ?>
63
+ </p>
64
+ <?php if ( ! empty($tosUrl)) : ?>
65
+ <div id="fermopoint_conditions" class="fermopoint-new-account">
66
+ <iframe src="<?php echo $tosUrl ?>">
67
+ <p><?php echo $this->__('Your browser does not support iframes.') ?></p>
68
+ </iframe>
69
+ <div style="clear: both"></div>
70
+ <p class="checkbox">
71
+ <input type="checkbox" name="fermopoint_accept_terms" id="fermopoint_accept_terms" value="1" autocomplete="off" class="required-entry" />
72
+ <label for="fermopoint_accept_terms"><?php echo $this->__('I accept Fermo!Point terms and conditions') ?></label>
73
+ </p>
74
+ </div>
75
+ <?php endif ?>
76
+ </div>
77
+ </div>
78
+ <script type="text/javascript">
79
+ //<![CDATA[
80
+
81
+ fpStorePickup.initGoogleMap('<?php echo $this->getGoogleMaps() ?>', 'fermopoint_google_map<?php echo $mapId ?>');
82
+ fpStorePickup.onInit = function () {
83
+ fpStorePickup.search($('fermopoint_search_address').value, $('fermopoint_search_radius').value);
84
+ };
85
+ fpStorePickup.onSearchStart = function () {
86
+ $('fermopoint_search').addClassName('in-progress').addClassName('disabled');
87
+ };
88
+ fpStorePickup.onSearchEnd = function (points, error) {
89
+ $('fermopoint_search').removeClassName('in-progress').removeClassName('disabled');
90
+ if (! error && ! points.length)
91
+ setTimeout(function () {
92
+ alert('<?php echo $this->__('No points found around given location') ?>');
93
+ }, 1);
94
+ };
95
+ fpStorePickup.onSelectPoint = function (point) {
96
+ $('fermopoint_point').show();
97
+ Effect.ScrollTo('fermopoint_point');
98
+ $('fermopoint_point_id').value = point.id;
99
+ $('fermopoint_point_name').innerHTML = point.name;
100
+ $('fermopoint_point_address').innerHTML = point.street
101
+ + '<br />' + point.postcode
102
+ + ' ' + point.city
103
+ + ' ' + point.region
104
+ //+ ' ' + point.country_id
105
+ ;
106
+ };
107
+
108
+ Event.observe('fermopoint_search', 'click', function (event) {
109
+ event.preventDefault();
110
+ if ($(this).hasClassName('in-progress')) {
111
+ alert('<?php echo $this->__('Another search is already running') ?>');
112
+ return;
113
+ }
114
+
115
+ fpStorePickup.search($('fermopoint_search_address').value, $('fermopoint_search_radius').value);
116
+ });
117
+ //]]>
118
+ </script>
app/etc/modules/FermoPoint_StorePickup.xml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <FermoPoint_StorePickup>
5
+ <active>true</active>
6
+ <codePool>community</codePool>
7
+ </FermoPoint_StorePickup>
8
+ </modules>
9
+ </config>
app/locale/it_IT/FermoPoint_StorePickup.csv ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Fermo!Point,Fermo!Point
2
+ Title,Titolo
3
+ Cost,Costo
4
+ Maximum Subtotal,Subtotale Massimo
5
+ Maximum Weight,Peso Massimo
6
+ Terms and Conditions Url,Url Termini e Condizioni
7
+ Google Maps API Key,Chiave API Google Maps
8
+ Applicable payments,Pagamenti Applicabili
9
+ Point,Punto
10
+ Please specify Merchant ID,Specificare il Merchant ID
11
+ Please specify API Key,Specificare l´API Key
12
+ Could not communicate with server,Impossibile comunicare col server
13
+ Invalid server reply,Risposta server non valida
14
+ Could not submit order,Non è stato possibile inviare l´ordine.
15
+ All Allowed Payments,Tutti i Pagamenti Consentiti
16
+ Specific Payments,Specifici Pagamenti
17
+ Invalid search parameters.,Parametri di ricerca non validi.
18
+ Error while communicating with Fermo!Point. Please retry later.,Si è verificato un errore durante la comunicazione con Fermo!Point. Riprovare a breve.
19
+ Location does not found,Location non trovata
20
+ Your browser does not support iframes.,Il tuo browser non supporta gli iframe.
21
+ Loading,Caricando
22
+ No points found around given location,Non è stato trovato alcun negozio Fermo!Point nelle vicinanze dell´indirizzo inserito. Prova ad aumentare il raggio di ricerca.
23
+ Another search is already running,Vi è già un´altra ricerca in corso.
24
+ You should accept Fermo!Point terms and conditions,E´ necessario accettare i termini e condizioni Fermo!Point
25
+ Distance,Distanza
26
+ Selected pick-up point,Hai selezionato il seguente punto di ritiro
27
+ You should select one of available pick-up points to continue,Selezionare un punto di ritiro dalla mappa prima di continuare
28
+ Select this pick-up point,Seleziona questo punto di ritiro
29
+ Radius,Raggio
30
+ Fermo!Point Store Pickup,"Ricevi il tuo acquisto in uno dei punti di ritiro Fermo!Point (<a target=""_blank"" href=""http://www.fermopoint.it/"">www.fermopoint.it</a>)"
31
+ I accept Fermo!Point terms and conditions,Utilizzando il servizio si accetta il Contratto
32
+ Search for pick-up points,Cerca un punto di ritiro
33
+ "You are viewing the collection points closest to the address below. If you want, you can enter a different address.","Stai visualizzando i punti di ritiro vicini al seguente indirizzo. <br/>Se vuoi, puoi inserire un indirizzo differente."
34
+ "I accept <a href=""http://www.fermopoint.it/termini-merchant/"" target=""_blank"">Fermo!Point Terms of Service</a>","Accetto <a href=""http://www.fermopoint.it/termini-merchant/"" target=""_blank"">Fermo!Point Termini di Servizio</a>"
35
+ Accept TOS,Accettare Termini di Servizio
36
+ Fermo!Point Point,Fermo!Point Point
37
+ "For all the information on installing and configuring the module and Fermo!Point Merchant Account, you can refer to <a href=""%s"" target=""_blank"">this page</a>","Per tutte le informazioni sull´installazione e la configurazione del modulo e Fermo!Point Merchant Account, è possibile fare riferimento a <a href=""%s"" target=""_blank"">questa pagina</a>"
38
+ Hours,Orari
39
+ "Warning! Do not select ""Cash on Delivery"" methods, as we do not support them","Attenzione! Non selezionare il contrassegno come metodo di pagamento compatibile, la merce non verrà ritirata"
40
+ Contact Person,Referente
41
+ Category,Categoria
42
+ I did not use Fermo!Point before,E´ la prima volta che uso Fermo!Point
43
+ I have used Fermo!Point in the past,Ho gi&agrave; utilizzato Fermo!Point
44
+ User with this nickname already exists,Nickname già in uso. Scieglierne un altro.
45
+ Mobile phone,Cellulare
46
+ Please enter your Nickname. The nickname will be required at the time of withdrawal of the package,Inserisci il tuo Nickname. Il Nickname ti verr&agrave; richiesto al momento del ritiro del pacco
47
+ Choose and enter your nickname. The nickname will be required at the time of withdrawal of the package,Scegli e inserisci il tuo Nickname. Il Nickname ti verr&agrave; richiesto al momento del ritiro del pacco
48
+ Enter your cell phone number for delivery,Inserire se si vuole ricevere la notifica dell´arrivo del pacco tramite SMS
49
+ There is no user with this nickname,Il Nickname inserito non risulta associato ad alcun utente.
50
+ All Orders,Tutte le Transazioni
51
+ "Service is not available at the moment, please try again later","Servizio al momento non disponibile"
52
+ "The billing information will be disclosed to Fermo!Point to register the new account and the user will be used in the future on any other site that integrates eCommerce service Fermo!Point","I dati di fatturazione saranno comunicati a Fermo!Point per la registrazione del nuovo account e l'utenza potrà essere utilizzata in futuro su qualsiasi altro sito eCommerce che integra il servizio Fermo!Point"
53
+ "To the email address specified in the billing information <b>%s</ b> will receive an email with your login credentials to the portal Fermo!Point","All’indirizzo email specificato nei dati di fatturazione <b>%s</b> riceverete un’email con le credenziali di accesso al portale Fermo!Point"
js/fermopoint/markerclusterer.js ADDED
@@ -0,0 +1,1316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // ==ClosureCompiler==
2
+ // @compilation_level ADVANCED_OPTIMIZATIONS
3
+ // @externs_url http://closure-compiler.googlecode.com/svn/trunk/contrib/externs/maps/google_maps_api_v3_3.js
4
+ // ==/ClosureCompiler==
5
+
6
+ /**
7
+ * @name MarkerClusterer for Google Maps v3
8
+ * @version version 1.0.1
9
+ * @author Luke Mahe
10
+ * @fileoverview
11
+ * The library creates and manages per-zoom-level clusters for large amounts of
12
+ * markers.
13
+ * <br/>
14
+ * This is a v3 implementation of the
15
+ * <a href="http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/"
16
+ * >v2 MarkerClusterer</a>.
17
+ */
18
+
19
+ /**
20
+ * Licensed under the Apache License, Version 2.0 (the "License");
21
+ * you may not use this file except in compliance with the License.
22
+ * You may obtain a copy of the License at
23
+ *
24
+ * http://www.apache.org/licenses/LICENSE-2.0
25
+ *
26
+ * Unless required by applicable law or agreed to in writing, software
27
+ * distributed under the License is distributed on an "AS IS" BASIS,
28
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29
+ * See the License for the specific language governing permissions and
30
+ * limitations under the License.
31
+ */
32
+
33
+
34
+ /**
35
+ * A Marker Clusterer that clusters markers.
36
+ *
37
+ * @param {google.maps.Map} map The Google map to attach to.
38
+ * @param {Array.<google.maps.Marker>=} opt_markers Optional markers to add to
39
+ * the cluster.
40
+ * @param {Object=} opt_options support the following options:
41
+ * 'gridSize': (number) The grid size of a cluster in pixels.
42
+ * 'maxZoom': (number) The maximum zoom level that a marker can be part of a
43
+ * cluster.
44
+ * 'zoomOnClick': (boolean) Whether the default behaviour of clicking on a
45
+ * cluster is to zoom into it.
46
+ * 'averageCenter': (boolean) Wether the center of each cluster should be
47
+ * the average of all markers in the cluster.
48
+ * 'minimumClusterSize': (number) The minimum number of markers to be in a
49
+ * cluster before the markers are hidden and a count
50
+ * is shown.
51
+ * 'styles': (object) An object that has style properties:
52
+ * 'url': (string) The image url.
53
+ * 'height': (number) The image height.
54
+ * 'width': (number) The image width.
55
+ * 'anchor': (Array) The anchor position of the label text.
56
+ * 'textColor': (string) The text color.
57
+ * 'textSize': (number) The text size.
58
+ * 'backgroundPosition': (string) The position of the backgound x, y.
59
+ * @constructor
60
+ * @extends google.maps.OverlayView
61
+ */
62
+ function MarkerClusterer(map, opt_markers, opt_options) {
63
+ // MarkerClusterer implements google.maps.OverlayView interface. We use the
64
+ // extend function to extend MarkerClusterer with google.maps.OverlayView
65
+ // because it might not always be available when the code is defined so we
66
+ // look for it at the last possible moment. If it doesn't exist now then
67
+ // there is no point going ahead :)
68
+ this.extend(MarkerClusterer, google.maps.OverlayView);
69
+ this.map_ = map;
70
+
71
+ /**
72
+ * @type {Array.<google.maps.Marker>}
73
+ * @private
74
+ */
75
+ this.markers_ = [];
76
+
77
+ /**
78
+ * @type {Array.<Cluster>}
79
+ */
80
+ this.clusters_ = [];
81
+
82
+ this.sizes = [53, 56, 66, 78, 90];
83
+
84
+ /**
85
+ * @private
86
+ */
87
+ this.styles_ = [];
88
+
89
+ /**
90
+ * @type {boolean}
91
+ * @private
92
+ */
93
+ this.ready_ = false;
94
+
95
+ var options = opt_options || {};
96
+
97
+ /**
98
+ * @type {number}
99
+ * @private
100
+ */
101
+ this.gridSize_ = options['gridSize'] || 60;
102
+
103
+ /**
104
+ * @private
105
+ */
106
+ this.minClusterSize_ = options['minimumClusterSize'] || 2;
107
+
108
+
109
+ /**
110
+ * @type {?number}
111
+ * @private
112
+ */
113
+ this.maxZoom_ = options['maxZoom'] || null;
114
+
115
+ this.styles_ = options['styles'] || [];
116
+
117
+ /**
118
+ * @type {string}
119
+ * @private
120
+ */
121
+ this.imagePath_ = options['imagePath'] ||
122
+ this.MARKER_CLUSTER_IMAGE_PATH_;
123
+
124
+ /**
125
+ * @type {string}
126
+ * @private
127
+ */
128
+ this.imageExtension_ = options['imageExtension'] ||
129
+ this.MARKER_CLUSTER_IMAGE_EXTENSION_;
130
+
131
+ /**
132
+ * @type {boolean}
133
+ * @private
134
+ */
135
+ this.zoomOnClick_ = true;
136
+
137
+ if (options['zoomOnClick'] != undefined) {
138
+ this.zoomOnClick_ = options['zoomOnClick'];
139
+ }
140
+
141
+ /**
142
+ * @type {boolean}
143
+ * @private
144
+ */
145
+ this.averageCenter_ = false;
146
+
147
+ if (options['averageCenter'] != undefined) {
148
+ this.averageCenter_ = options['averageCenter'];
149
+ }
150
+
151
+ this.setupStyles_();
152
+
153
+ this.setMap(map);
154
+
155
+ /**
156
+ * @type {number}
157
+ * @private
158
+ */
159
+ this.prevZoom_ = this.map_.getZoom();
160
+
161
+ // Add the map event listeners
162
+ var that = this;
163
+ google.maps.event.addListener(this.map_, 'zoom_changed', function() {
164
+ // Determines map type and prevent illegal zoom levels
165
+ var zoom = that.map_.getZoom();
166
+ var minZoom = that.map_.minZoom || 0;
167
+ var maxZoom = Math.min(that.map_.maxZoom || 100,
168
+ that.map_.mapTypes[that.map_.getMapTypeId()].maxZoom);
169
+ zoom = Math.min(Math.max(zoom,minZoom),maxZoom);
170
+
171
+ if (that.prevZoom_ != zoom) {
172
+ that.prevZoom_ = zoom;
173
+ that.resetViewport();
174
+ }
175
+ });
176
+
177
+ google.maps.event.addListener(this.map_, 'idle', function() {
178
+ that.redraw();
179
+ });
180
+
181
+ // Finally, add the markers
182
+ if (opt_markers && (opt_markers.length || Object.keys(opt_markers).length)) {
183
+ this.addMarkers(opt_markers, false);
184
+ }
185
+ }
186
+
187
+
188
+ /**
189
+ * The marker cluster image path.
190
+ *
191
+ * @type {string}
192
+ * @private
193
+ */
194
+ MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_ =
195
+ 'http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/' +
196
+ 'images/m';
197
+
198
+
199
+ /**
200
+ * The marker cluster image path.
201
+ *
202
+ * @type {string}
203
+ * @private
204
+ */
205
+ MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_ = 'png';
206
+
207
+
208
+ /**
209
+ * Extends a objects prototype by anothers.
210
+ *
211
+ * @param {Object} obj1 The object to be extended.
212
+ * @param {Object} obj2 The object to extend with.
213
+ * @return {Object} The new extended object.
214
+ * @ignore
215
+ */
216
+ MarkerClusterer.prototype.extend = function(obj1, obj2) {
217
+ return (function(object) {
218
+ for (var property in object.prototype) {
219
+ this.prototype[property] = object.prototype[property];
220
+ }
221
+ return this;
222
+ }).apply(obj1, [obj2]);
223
+ };
224
+
225
+
226
+ /**
227
+ * Implementaion of the interface method.
228
+ * @ignore
229
+ */
230
+ MarkerClusterer.prototype.onAdd = function() {
231
+ this.setReady_(true);
232
+ };
233
+
234
+ /**
235
+ * Implementaion of the interface method.
236
+ * @ignore
237
+ */
238
+ MarkerClusterer.prototype.draw = function() {};
239
+
240
+ /**
241
+ * Sets up the styles object.
242
+ *
243
+ * @private
244
+ */
245
+ MarkerClusterer.prototype.setupStyles_ = function() {
246
+ if (this.styles_.length) {
247
+ return;
248
+ }
249
+
250
+ for (var i = 0, size; size = this.sizes[i]; i++) {
251
+ this.styles_.push({
252
+ url: this.imagePath_ + (i + 1) + '.' + this.imageExtension_,
253
+ height: size,
254
+ width: size
255
+ });
256
+ }
257
+ };
258
+
259
+ /**
260
+ * Fit the map to the bounds of the markers in the clusterer.
261
+ */
262
+ MarkerClusterer.prototype.fitMapToMarkers = function() {
263
+ var markers = this.getMarkers();
264
+ var bounds = new google.maps.LatLngBounds();
265
+ for (var i = 0, marker; marker = markers[i]; i++) {
266
+ bounds.extend(marker.getPosition());
267
+ }
268
+
269
+ this.map_.fitBounds(bounds);
270
+ };
271
+
272
+
273
+ /**
274
+ * Sets the styles.
275
+ *
276
+ * @param {Object} styles The style to set.
277
+ */
278
+ MarkerClusterer.prototype.setStyles = function(styles) {
279
+ this.styles_ = styles;
280
+ };
281
+
282
+
283
+ /**
284
+ * Gets the styles.
285
+ *
286
+ * @return {Object} The styles object.
287
+ */
288
+ MarkerClusterer.prototype.getStyles = function() {
289
+ return this.styles_;
290
+ };
291
+
292
+
293
+ /**
294
+ * Whether zoom on click is set.
295
+ *
296
+ * @return {boolean} True if zoomOnClick_ is set.
297
+ */
298
+ MarkerClusterer.prototype.isZoomOnClick = function() {
299
+ return this.zoomOnClick_;
300
+ };
301
+
302
+ /**
303
+ * Whether average center is set.
304
+ *
305
+ * @return {boolean} True if averageCenter_ is set.
306
+ */
307
+ MarkerClusterer.prototype.isAverageCenter = function() {
308
+ return this.averageCenter_;
309
+ };
310
+
311
+
312
+ /**
313
+ * Returns the array of markers in the clusterer.
314
+ *
315
+ * @return {Array.<google.maps.Marker>} The markers.
316
+ */
317
+ MarkerClusterer.prototype.getMarkers = function() {
318
+ return this.markers_;
319
+ };
320
+
321
+
322
+ /**
323
+ * Returns the number of markers in the clusterer
324
+ *
325
+ * @return {Number} The number of markers.
326
+ */
327
+ MarkerClusterer.prototype.getTotalMarkers = function() {
328
+ return this.markers_.length;
329
+ };
330
+
331
+
332
+ /**
333
+ * Sets the max zoom for the clusterer.
334
+ *
335
+ * @param {number} maxZoom The max zoom level.
336
+ */
337
+ MarkerClusterer.prototype.setMaxZoom = function(maxZoom) {
338
+ this.maxZoom_ = maxZoom;
339
+ };
340
+
341
+
342
+ /**
343
+ * Gets the max zoom for the clusterer.
344
+ *
345
+ * @return {number} The max zoom level.
346
+ */
347
+ MarkerClusterer.prototype.getMaxZoom = function() {
348
+ return this.maxZoom_;
349
+ };
350
+
351
+
352
+ /**
353
+ * The function for calculating the cluster icon image.
354
+ *
355
+ * @param {Array.<google.maps.Marker>} markers The markers in the clusterer.
356
+ * @param {number} numStyles The number of styles available.
357
+ * @return {Object} A object properties: 'text' (string) and 'index' (number).
358
+ * @private
359
+ */
360
+ MarkerClusterer.prototype.calculator_ = function(markers, numStyles) {
361
+ var index = 0;
362
+ var count = markers.length;
363
+ var dv = count;
364
+ while (dv !== 0) {
365
+ dv = parseInt(dv / 10, 10);
366
+ index++;
367
+ }
368
+
369
+ index = Math.min(index, numStyles);
370
+ return {
371
+ text: count,
372
+ index: index
373
+ };
374
+ };
375
+
376
+
377
+ /**
378
+ * Set the calculator function.
379
+ *
380
+ * @param {function(Array, number)} calculator The function to set as the
381
+ * calculator. The function should return a object properties:
382
+ * 'text' (string) and 'index' (number).
383
+ *
384
+ */
385
+ MarkerClusterer.prototype.setCalculator = function(calculator) {
386
+ this.calculator_ = calculator;
387
+ };
388
+
389
+
390
+ /**
391
+ * Get the calculator function.
392
+ *
393
+ * @return {function(Array, number)} the calculator function.
394
+ */
395
+ MarkerClusterer.prototype.getCalculator = function() {
396
+ return this.calculator_;
397
+ };
398
+
399
+
400
+ /**
401
+ * Add an array of markers to the clusterer.
402
+ *
403
+ * @param {Array.<google.maps.Marker>} markers The markers to add.
404
+ * @param {boolean=} opt_nodraw Whether to redraw the clusters.
405
+ */
406
+ MarkerClusterer.prototype.addMarkers = function(markers, opt_nodraw) {
407
+ if (markers.length) {
408
+ for (var i = 0, marker; marker = markers[i]; i++) {
409
+ this.pushMarkerTo_(marker);
410
+ }
411
+ } else if (Object.keys(markers).length) {
412
+ for (var marker in markers) {
413
+ this.pushMarkerTo_(markers[marker]);
414
+ }
415
+ }
416
+ if (!opt_nodraw) {
417
+ this.redraw();
418
+ }
419
+ };
420
+
421
+
422
+ /**
423
+ * Pushes a marker to the clusterer.
424
+ *
425
+ * @param {google.maps.Marker} marker The marker to add.
426
+ * @private
427
+ */
428
+ MarkerClusterer.prototype.pushMarkerTo_ = function(marker) {
429
+ marker.isAdded = false;
430
+ if (marker['draggable']) {
431
+ // If the marker is draggable add a listener so we update the clusters on
432
+ // the drag end.
433
+ var that = this;
434
+ google.maps.event.addListener(marker, 'dragend', function() {
435
+ marker.isAdded = false;
436
+ that.repaint();
437
+ });
438
+ }
439
+ this.markers_.push(marker);
440
+ };
441
+
442
+
443
+ /**
444
+ * Adds a marker to the clusterer and redraws if needed.
445
+ *
446
+ * @param {google.maps.Marker} marker The marker to add.
447
+ * @param {boolean=} opt_nodraw Whether to redraw the clusters.
448
+ */
449
+ MarkerClusterer.prototype.addMarker = function(marker, opt_nodraw) {
450
+ this.pushMarkerTo_(marker);
451
+ if (!opt_nodraw) {
452
+ this.redraw();
453
+ }
454
+ };
455
+
456
+
457
+ /**
458
+ * Removes a marker and returns true if removed, false if not
459
+ *
460
+ * @param {google.maps.Marker} marker The marker to remove
461
+ * @return {boolean} Whether the marker was removed or not
462
+ * @private
463
+ */
464
+ MarkerClusterer.prototype.removeMarker_ = function(marker) {
465
+ var index = -1;
466
+ if (this.markers_.indexOf) {
467
+ index = this.markers_.indexOf(marker);
468
+ } else {
469
+ for (var i = 0, m; m = this.markers_[i]; i++) {
470
+ if (m == marker) {
471
+ index = i;
472
+ break;
473
+ }
474
+ }
475
+ }
476
+
477
+ if (index == -1) {
478
+ // Marker is not in our list of markers.
479
+ return false;
480
+ }
481
+
482
+ marker.setMap(null);
483
+
484
+ this.markers_.splice(index, 1);
485
+
486
+ return true;
487
+ };
488
+
489
+
490
+ /**
491
+ * Remove a marker from the cluster.
492
+ *
493
+ * @param {google.maps.Marker} marker The marker to remove.
494
+ * @param {boolean=} opt_nodraw Optional boolean to force no redraw.
495
+ * @return {boolean} True if the marker was removed.
496
+ */
497
+ MarkerClusterer.prototype.removeMarker = function(marker, opt_nodraw) {
498
+ var removed = this.removeMarker_(marker);
499
+
500
+ if (!opt_nodraw && removed) {
501
+ this.resetViewport();
502
+ this.redraw();
503
+ return true;
504
+ } else {
505
+ return false;
506
+ }
507
+ };
508
+
509
+
510
+ /**
511
+ * Removes an array of markers from the cluster.
512
+ *
513
+ * @param {Array.<google.maps.Marker>} markers The markers to remove.
514
+ * @param {boolean=} opt_nodraw Optional boolean to force no redraw.
515
+ */
516
+ MarkerClusterer.prototype.removeMarkers = function(markers, opt_nodraw) {
517
+ var removed = false;
518
+
519
+ for (var i = 0, marker; marker = markers[i]; i++) {
520
+ var r = this.removeMarker_(marker);
521
+ removed = removed || r;
522
+ }
523
+
524
+ if (!opt_nodraw && removed) {
525
+ this.resetViewport();
526
+ this.redraw();
527
+ return true;
528
+ }
529
+ };
530
+
531
+
532
+ /**
533
+ * Sets the clusterer's ready state.
534
+ *
535
+ * @param {boolean} ready The state.
536
+ * @private
537
+ */
538
+ MarkerClusterer.prototype.setReady_ = function(ready) {
539
+ if (!this.ready_) {
540
+ this.ready_ = ready;
541
+ this.createClusters_();
542
+ }
543
+ };
544
+
545
+
546
+ /**
547
+ * Returns the number of clusters in the clusterer.
548
+ *
549
+ * @return {number} The number of clusters.
550
+ */
551
+ MarkerClusterer.prototype.getTotalClusters = function() {
552
+ return this.clusters_.length;
553
+ };
554
+
555
+
556
+ /**
557
+ * Returns the google map that the clusterer is associated with.
558
+ *
559
+ * @return {google.maps.Map} The map.
560
+ */
561
+ MarkerClusterer.prototype.getMap = function() {
562
+ return this.map_;
563
+ };
564
+
565
+
566
+ /**
567
+ * Sets the google map that the clusterer is associated with.
568
+ *
569
+ * @param {google.maps.Map} map The map.
570
+ */
571
+ MarkerClusterer.prototype.setMap = function(map) {
572
+ this.map_ = map;
573
+ };
574
+
575
+
576
+ /**
577
+ * Returns the size of the grid.
578
+ *
579
+ * @return {number} The grid size.
580
+ */
581
+ MarkerClusterer.prototype.getGridSize = function() {
582
+ return this.gridSize_;
583
+ };
584
+
585
+
586
+ /**
587
+ * Sets the size of the grid.
588
+ *
589
+ * @param {number} size The grid size.
590
+ */
591
+ MarkerClusterer.prototype.setGridSize = function(size) {
592
+ this.gridSize_ = size;
593
+ };
594
+
595
+
596
+ /**
597
+ * Returns the min cluster size.
598
+ *
599
+ * @return {number} The grid size.
600
+ */
601
+ MarkerClusterer.prototype.getMinClusterSize = function() {
602
+ return this.minClusterSize_;
603
+ };
604
+
605
+ /**
606
+ * Sets the min cluster size.
607
+ *
608
+ * @param {number} size The grid size.
609
+ */
610
+ MarkerClusterer.prototype.setMinClusterSize = function(size) {
611
+ this.minClusterSize_ = size;
612
+ };
613
+
614
+
615
+ /**
616
+ * Extends a bounds object by the grid size.
617
+ *
618
+ * @param {google.maps.LatLngBounds} bounds The bounds to extend.
619
+ * @return {google.maps.LatLngBounds} The extended bounds.
620
+ */
621
+ MarkerClusterer.prototype.getExtendedBounds = function(bounds) {
622
+ var projection = this.getProjection();
623
+
624
+ // Turn the bounds into latlng.
625
+ var tr = new google.maps.LatLng(bounds.getNorthEast().lat(),
626
+ bounds.getNorthEast().lng());
627
+ var bl = new google.maps.LatLng(bounds.getSouthWest().lat(),
628
+ bounds.getSouthWest().lng());
629
+
630
+ // Convert the points to pixels and the extend out by the grid size.
631
+ var trPix = projection.fromLatLngToDivPixel(tr);
632
+ trPix.x += this.gridSize_;
633
+ trPix.y -= this.gridSize_;
634
+
635
+ var blPix = projection.fromLatLngToDivPixel(bl);
636
+ blPix.x -= this.gridSize_;
637
+ blPix.y += this.gridSize_;
638
+
639
+ // Convert the pixel points back to LatLng
640
+ var ne = projection.fromDivPixelToLatLng(trPix);
641
+ var sw = projection.fromDivPixelToLatLng(blPix);
642
+
643
+ // Extend the bounds to contain the new bounds.
644
+ bounds.extend(ne);
645
+ bounds.extend(sw);
646
+
647
+ return bounds;
648
+ };
649
+
650
+
651
+ /**
652
+ * Determins if a marker is contained in a bounds.
653
+ *
654
+ * @param {google.maps.Marker} marker The marker to check.
655
+ * @param {google.maps.LatLngBounds} bounds The bounds to check against.
656
+ * @return {boolean} True if the marker is in the bounds.
657
+ * @private
658
+ */
659
+ MarkerClusterer.prototype.isMarkerInBounds_ = function(marker, bounds) {
660
+ return bounds.contains(marker.getPosition());
661
+ };
662
+
663
+
664
+ /**
665
+ * Clears all clusters and markers from the clusterer.
666
+ */
667
+ MarkerClusterer.prototype.clearMarkers = function() {
668
+ this.resetViewport(true);
669
+
670
+ // Set the markers a empty array.
671
+ this.markers_ = [];
672
+ };
673
+
674
+
675
+ /**
676
+ * Clears all existing clusters and recreates them.
677
+ * @param {boolean} opt_hide To also hide the marker.
678
+ */
679
+ MarkerClusterer.prototype.resetViewport = function(opt_hide) {
680
+ // Remove all the clusters
681
+ for (var i = 0, cluster; cluster = this.clusters_[i]; i++) {
682
+ cluster.remove();
683
+ }
684
+
685
+ // Reset the markers to not be added and to be invisible.
686
+ for (var i = 0, marker; marker = this.markers_[i]; i++) {
687
+ marker.isAdded = false;
688
+ if (opt_hide) {
689
+ marker.setMap(null);
690
+ }
691
+ }
692
+
693
+ this.clusters_ = [];
694
+ };
695
+
696
+ /**
697
+ *
698
+ */
699
+ MarkerClusterer.prototype.repaint = function() {
700
+ var oldClusters = this.clusters_.slice();
701
+ this.clusters_.length = 0;
702
+ this.resetViewport();
703
+ this.redraw();
704
+
705
+ // Remove the old clusters.
706
+ // Do it in a timeout so the other clusters have been drawn first.
707
+ window.setTimeout(function() {
708
+ for (var i = 0, cluster; cluster = oldClusters[i]; i++) {
709
+ cluster.remove();
710
+ }
711
+ }, 0);
712
+ };
713
+
714
+
715
+ /**
716
+ * Redraws the clusters.
717
+ */
718
+ MarkerClusterer.prototype.redraw = function() {
719
+ this.createClusters_();
720
+ };
721
+
722
+
723
+ /**
724
+ * Calculates the distance between two latlng locations in km.
725
+ * @see http://www.movable-type.co.uk/scripts/latlong.html
726
+ *
727
+ * @param {google.maps.LatLng} p1 The first lat lng point.
728
+ * @param {google.maps.LatLng} p2 The second lat lng point.
729
+ * @return {number} The distance between the two points in km.
730
+ * @private
731
+ */
732
+ MarkerClusterer.prototype.distanceBetweenPoints_ = function(p1, p2) {
733
+ if (!p1 || !p2) {
734
+ return 0;
735
+ }
736
+
737
+ var R = 6371; // Radius of the Earth in km
738
+ var dLat = (p2.lat() - p1.lat()) * Math.PI / 180;
739
+ var dLon = (p2.lng() - p1.lng()) * Math.PI / 180;
740
+ var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
741
+ Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) *
742
+ Math.sin(dLon / 2) * Math.sin(dLon / 2);
743
+ var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
744
+ var d = R * c;
745
+ return d;
746
+ };
747
+
748
+
749
+ /**
750
+ * Add a marker to a cluster, or creates a new cluster.
751
+ *
752
+ * @param {google.maps.Marker} marker The marker to add.
753
+ * @private
754
+ */
755
+ MarkerClusterer.prototype.addToClosestCluster_ = function(marker) {
756
+ var distance = 40000; // Some large number
757
+ var clusterToAddTo = null;
758
+ var pos = marker.getPosition();
759
+ for (var i = 0, cluster; cluster = this.clusters_[i]; i++) {
760
+ var center = cluster.getCenter();
761
+ if (center) {
762
+ var d = this.distanceBetweenPoints_(center, marker.getPosition());
763
+ if (d < distance) {
764
+ distance = d;
765
+ clusterToAddTo = cluster;
766
+ }
767
+ }
768
+ }
769
+
770
+ if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) {
771
+ clusterToAddTo.addMarker(marker);
772
+ } else {
773
+ var cluster = new Cluster(this);
774
+ cluster.addMarker(marker);
775
+ this.clusters_.push(cluster);
776
+ }
777
+ };
778
+
779
+
780
+ /**
781
+ * Creates the clusters.
782
+ *
783
+ * @private
784
+ */
785
+ MarkerClusterer.prototype.createClusters_ = function() {
786
+ if (!this.ready_) {
787
+ return;
788
+ }
789
+
790
+ // Get our current map view bounds.
791
+ // Create a new bounds object so we don't affect the map.
792
+ var mapBounds = new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(),
793
+ this.map_.getBounds().getNorthEast());
794
+ var bounds = this.getExtendedBounds(mapBounds);
795
+
796
+ for (var i = 0, marker; marker = this.markers_[i]; i++) {
797
+ if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) {
798
+ this.addToClosestCluster_(marker);
799
+ }
800
+ }
801
+ };
802
+
803
+
804
+ /**
805
+ * A cluster that contains markers.
806
+ *
807
+ * @param {MarkerClusterer} markerClusterer The markerclusterer that this
808
+ * cluster is associated with.
809
+ * @constructor
810
+ * @ignore
811
+ */
812
+ function Cluster(markerClusterer) {
813
+ this.markerClusterer_ = markerClusterer;
814
+ this.map_ = markerClusterer.getMap();
815
+ this.gridSize_ = markerClusterer.getGridSize();
816
+ this.minClusterSize_ = markerClusterer.getMinClusterSize();
817
+ this.averageCenter_ = markerClusterer.isAverageCenter();
818
+ this.center_ = null;
819
+ this.markers_ = [];
820
+ this.bounds_ = null;
821
+ this.clusterIcon_ = new ClusterIcon(this, markerClusterer.getStyles(),
822
+ markerClusterer.getGridSize());
823
+ }
824
+
825
+ /**
826
+ * Determins if a marker is already added to the cluster.
827
+ *
828
+ * @param {google.maps.Marker} marker The marker to check.
829
+ * @return {boolean} True if the marker is already added.
830
+ */
831
+ Cluster.prototype.isMarkerAlreadyAdded = function(marker) {
832
+ if (this.markers_.indexOf) {
833
+ return this.markers_.indexOf(marker) != -1;
834
+ } else {
835
+ for (var i = 0, m; m = this.markers_[i]; i++) {
836
+ if (m == marker) {
837
+ return true;
838
+ }
839
+ }
840
+ }
841
+ return false;
842
+ };
843
+
844
+
845
+ /**
846
+ * Add a marker the cluster.
847
+ *
848
+ * @param {google.maps.Marker} marker The marker to add.
849
+ * @return {boolean} True if the marker was added.
850
+ */
851
+ Cluster.prototype.addMarker = function(marker) {
852
+ if (this.isMarkerAlreadyAdded(marker)) {
853
+ return false;
854
+ }
855
+
856
+ if (!this.center_) {
857
+ this.center_ = marker.getPosition();
858
+ this.calculateBounds_();
859
+ } else {
860
+ if (this.averageCenter_) {
861
+ var l = this.markers_.length + 1;
862
+ var lat = (this.center_.lat() * (l-1) + marker.getPosition().lat()) / l;
863
+ var lng = (this.center_.lng() * (l-1) + marker.getPosition().lng()) / l;
864
+ this.center_ = new google.maps.LatLng(lat, lng);
865
+ this.calculateBounds_();
866
+ }
867
+ }
868
+
869
+ marker.isAdded = true;
870
+ this.markers_.push(marker);
871
+
872
+ var len = this.markers_.length;
873
+ if (len < this.minClusterSize_ && marker.getMap() != this.map_) {
874
+ // Min cluster size not reached so show the marker.
875
+ marker.setMap(this.map_);
876
+ }
877
+
878
+ if (len == this.minClusterSize_) {
879
+ // Hide the markers that were showing.
880
+ for (var i = 0; i < len; i++) {
881
+ this.markers_[i].setMap(null);
882
+ }
883
+ }
884
+
885
+ if (len >= this.minClusterSize_) {
886
+ marker.setMap(null);
887
+ }
888
+
889
+ this.updateIcon();
890
+ return true;
891
+ };
892
+
893
+
894
+ /**
895
+ * Returns the marker clusterer that the cluster is associated with.
896
+ *
897
+ * @return {MarkerClusterer} The associated marker clusterer.
898
+ */
899
+ Cluster.prototype.getMarkerClusterer = function() {
900
+ return this.markerClusterer_;
901
+ };
902
+
903
+
904
+ /**
905
+ * Returns the bounds of the cluster.
906
+ *
907
+ * @return {google.maps.LatLngBounds} the cluster bounds.
908
+ */
909
+ Cluster.prototype.getBounds = function() {
910
+ var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
911
+ var markers = this.getMarkers();
912
+ for (var i = 0, marker; marker = markers[i]; i++) {
913
+ bounds.extend(marker.getPosition());
914
+ }
915
+ return bounds;
916
+ };
917
+
918
+
919
+ /**
920
+ * Removes the cluster
921
+ */
922
+ Cluster.prototype.remove = function() {
923
+ this.clusterIcon_.remove();
924
+ this.markers_.length = 0;
925
+ delete this.markers_;
926
+ };
927
+
928
+
929
+ /**
930
+ * Returns the center of the cluster.
931
+ *
932
+ * @return {number} The cluster center.
933
+ */
934
+ Cluster.prototype.getSize = function() {
935
+ return this.markers_.length;
936
+ };
937
+
938
+
939
+ /**
940
+ * Returns the center of the cluster.
941
+ *
942
+ * @return {Array.<google.maps.Marker>} The cluster center.
943
+ */
944
+ Cluster.prototype.getMarkers = function() {
945
+ return this.markers_;
946
+ };
947
+
948
+
949
+ /**
950
+ * Returns the center of the cluster.
951
+ *
952
+ * @return {google.maps.LatLng} The cluster center.
953
+ */
954
+ Cluster.prototype.getCenter = function() {
955
+ return this.center_;
956
+ };
957
+
958
+
959
+ /**
960
+ * Calculated the extended bounds of the cluster with the grid.
961
+ *
962
+ * @private
963
+ */
964
+ Cluster.prototype.calculateBounds_ = function() {
965
+ var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
966
+ this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds);
967
+ };
968
+
969
+
970
+ /**
971
+ * Determines if a marker lies in the clusters bounds.
972
+ *
973
+ * @param {google.maps.Marker} marker The marker to check.
974
+ * @return {boolean} True if the marker lies in the bounds.
975
+ */
976
+ Cluster.prototype.isMarkerInClusterBounds = function(marker) {
977
+ return this.bounds_.contains(marker.getPosition());
978
+ };
979
+
980
+
981
+ /**
982
+ * Returns the map that the cluster is associated with.
983
+ *
984
+ * @return {google.maps.Map} The map.
985
+ */
986
+ Cluster.prototype.getMap = function() {
987
+ return this.map_;
988
+ };
989
+
990
+
991
+ /**
992
+ * Updates the cluster icon
993
+ */
994
+ Cluster.prototype.updateIcon = function() {
995
+ var zoom = this.map_.getZoom();
996
+ var mz = this.markerClusterer_.getMaxZoom();
997
+
998
+ if (mz && zoom > mz) {
999
+ // The zoom is greater than our max zoom so show all the markers in cluster.
1000
+ for (var i = 0, marker; marker = this.markers_[i]; i++) {
1001
+ marker.setMap(this.map_);
1002
+ }
1003
+ return;
1004
+ }
1005
+
1006
+ if (this.markers_.length < this.minClusterSize_) {
1007
+ // Min cluster size not yet reached.
1008
+ this.clusterIcon_.hide();
1009
+ return;
1010
+ }
1011
+
1012
+ var numStyles = this.markerClusterer_.getStyles().length;
1013
+ var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles);
1014
+ this.clusterIcon_.setCenter(this.center_);
1015
+ this.clusterIcon_.setSums(sums);
1016
+ this.clusterIcon_.show();
1017
+ };
1018
+
1019
+
1020
+ /**
1021
+ * A cluster icon
1022
+ *
1023
+ * @param {Cluster} cluster The cluster to be associated with.
1024
+ * @param {Object} styles An object that has style properties:
1025
+ * 'url': (string) The image url.
1026
+ * 'height': (number) The image height.
1027
+ * 'width': (number) The image width.
1028
+ * 'anchor': (Array) The anchor position of the label text.
1029
+ * 'textColor': (string) The text color.
1030
+ * 'textSize': (number) The text size.
1031
+ * 'backgroundPosition: (string) The background postition x, y.
1032
+ * @param {number=} opt_padding Optional padding to apply to the cluster icon.
1033
+ * @constructor
1034
+ * @extends google.maps.OverlayView
1035
+ * @ignore
1036
+ */
1037
+ function ClusterIcon(cluster, styles, opt_padding) {
1038
+ cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView);
1039
+
1040
+ this.styles_ = styles;
1041
+ this.padding_ = opt_padding || 0;
1042
+ this.cluster_ = cluster;
1043
+ this.center_ = null;
1044
+ this.map_ = cluster.getMap();
1045
+ this.div_ = null;
1046
+ this.sums_ = null;
1047
+ this.visible_ = false;
1048
+
1049
+ this.setMap(this.map_);
1050
+ }
1051
+
1052
+
1053
+ /**
1054
+ * Triggers the clusterclick event and zoom's if the option is set.
1055
+ */
1056
+ ClusterIcon.prototype.triggerClusterClick = function() {
1057
+ var markerClusterer = this.cluster_.getMarkerClusterer();
1058
+
1059
+ // Trigger the clusterclick event.
1060
+ google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster_);
1061
+
1062
+ if (markerClusterer.isZoomOnClick()) {
1063
+ // Zoom into the cluster.
1064
+ this.map_.fitBounds(this.cluster_.getBounds());
1065
+ }
1066
+ };
1067
+
1068
+
1069
+ /**
1070
+ * Adding the cluster icon to the dom.
1071
+ * @ignore
1072
+ */
1073
+ ClusterIcon.prototype.onAdd = function() {
1074
+ this.div_ = document.createElement('DIV');
1075
+ if (this.visible_) {
1076
+ var pos = this.getPosFromLatLng_(this.center_);
1077
+ this.div_.style.cssText = this.createCss(pos);
1078
+ this.div_.innerHTML = this.sums_.text;
1079
+ }
1080
+
1081
+ var panes = this.getPanes();
1082
+ panes.overlayMouseTarget.appendChild(this.div_);
1083
+
1084
+ var that = this;
1085
+ google.maps.event.addDomListener(this.div_, 'click', function() {
1086
+ that.triggerClusterClick();
1087
+ });
1088
+ };
1089
+
1090
+
1091
+ /**
1092
+ * Returns the position to place the div dending on the latlng.
1093
+ *
1094
+ * @param {google.maps.LatLng} latlng The position in latlng.
1095
+ * @return {google.maps.Point} The position in pixels.
1096
+ * @private
1097
+ */
1098
+ ClusterIcon.prototype.getPosFromLatLng_ = function(latlng) {
1099
+ var pos = this.getProjection().fromLatLngToDivPixel(latlng);
1100
+ pos.x -= parseInt(this.width_ / 2, 10);
1101
+ pos.y -= parseInt(this.height_ / 2, 10);
1102
+ return pos;
1103
+ };
1104
+
1105
+
1106
+ /**
1107
+ * Draw the icon.
1108
+ * @ignore
1109
+ */
1110
+ ClusterIcon.prototype.draw = function() {
1111
+ if (this.visible_) {
1112
+ var pos = this.getPosFromLatLng_(this.center_);
1113
+ this.div_.style.top = pos.y + 'px';
1114
+ this.div_.style.left = pos.x + 'px';
1115
+ }
1116
+ };
1117
+
1118
+
1119
+ /**
1120
+ * Hide the icon.
1121
+ */
1122
+ ClusterIcon.prototype.hide = function() {
1123
+ if (this.div_) {
1124
+ this.div_.style.display = 'none';
1125
+ }
1126
+ this.visible_ = false;
1127
+ };
1128
+
1129
+
1130
+ /**
1131
+ * Position and show the icon.
1132
+ */
1133
+ ClusterIcon.prototype.show = function() {
1134
+ if (this.div_) {
1135
+ var pos = this.getPosFromLatLng_(this.center_);
1136
+ this.div_.style.cssText = this.createCss(pos);
1137
+ this.div_.style.display = '';
1138
+ }
1139
+ this.visible_ = true;
1140
+ };
1141
+
1142
+
1143
+ /**
1144
+ * Remove the icon from the map
1145
+ */
1146
+ ClusterIcon.prototype.remove = function() {
1147
+ this.setMap(null);
1148
+ };
1149
+
1150
+
1151
+ /**
1152
+ * Implementation of the onRemove interface.
1153
+ * @ignore
1154
+ */
1155
+ ClusterIcon.prototype.onRemove = function() {
1156
+ if (this.div_ && this.div_.parentNode) {
1157
+ this.hide();
1158
+ this.div_.parentNode.removeChild(this.div_);
1159
+ this.div_ = null;
1160
+ }
1161
+ };
1162
+
1163
+
1164
+ /**
1165
+ * Set the sums of the icon.
1166
+ *
1167
+ * @param {Object} sums The sums containing:
1168
+ * 'text': (string) The text to display in the icon.
1169
+ * 'index': (number) The style index of the icon.
1170
+ */
1171
+ ClusterIcon.prototype.setSums = function(sums) {
1172
+ this.sums_ = sums;
1173
+ this.text_ = sums.text;
1174
+ this.index_ = sums.index;
1175
+ if (this.div_) {
1176
+ this.div_.innerHTML = sums.text;
1177
+ }
1178
+
1179
+ this.useStyle();
1180
+ };
1181
+
1182
+
1183
+ /**
1184
+ * Sets the icon to the the styles.
1185
+ */
1186
+ ClusterIcon.prototype.useStyle = function() {
1187
+ var index = Math.max(0, this.sums_.index - 1);
1188
+ index = Math.min(this.styles_.length - 1, index);
1189
+ var style = this.styles_[index];
1190
+ this.url_ = style['url'];
1191
+ this.height_ = style['height'];
1192
+ this.width_ = style['width'];
1193
+ this.textColor_ = style['textColor'];
1194
+ this.anchor_ = style['anchor'];
1195
+ this.textSize_ = style['textSize'];
1196
+ this.backgroundPosition_ = style['backgroundPosition'];
1197
+ };
1198
+
1199
+
1200
+ /**
1201
+ * Sets the center of the icon.
1202
+ *
1203
+ * @param {google.maps.LatLng} center The latlng to set as the center.
1204
+ */
1205
+ ClusterIcon.prototype.setCenter = function(center) {
1206
+ this.center_ = center;
1207
+ };
1208
+
1209
+
1210
+ /**
1211
+ * Create the css text based on the position of the icon.
1212
+ *
1213
+ * @param {google.maps.Point} pos The position.
1214
+ * @return {string} The css style text.
1215
+ */
1216
+ ClusterIcon.prototype.createCss = function(pos) {
1217
+ var style = [];
1218
+ style.push('background-image:url(' + this.url_ + ');');
1219
+ var backgroundPosition = this.backgroundPosition_ ? this.backgroundPosition_ : '0 0';
1220
+ style.push('background-position:' + backgroundPosition + ';');
1221
+
1222
+ if (typeof this.anchor_ === 'object') {
1223
+ if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 &&
1224
+ this.anchor_[0] < this.height_) {
1225
+ style.push('height:' + (this.height_ - this.anchor_[0]) +
1226
+ 'px; padding-top:' + this.anchor_[0] + 'px;');
1227
+ } else {
1228
+ style.push('height:' + this.height_ + 'px; line-height:' + this.height_ +
1229
+ 'px;');
1230
+ }
1231
+ if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 &&
1232
+ this.anchor_[1] < this.width_) {
1233
+ style.push('width:' + (this.width_ - this.anchor_[1]) +
1234
+ 'px; padding-left:' + this.anchor_[1] + 'px;');
1235
+ } else {
1236
+ style.push('width:' + this.width_ + 'px; text-align:center;');
1237
+ }
1238
+ } else {
1239
+ style.push('height:' + this.height_ + 'px; line-height:' +
1240
+ this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;');
1241
+ }
1242
+
1243
+ var txtColor = this.textColor_ ? this.textColor_ : 'black';
1244
+ var txtSize = this.textSize_ ? this.textSize_ : 11;
1245
+
1246
+ style.push('cursor:pointer; top:' + pos.y + 'px; left:' +
1247
+ pos.x + 'px; color:' + txtColor + '; position:absolute; font-size:' +
1248
+ txtSize + 'px; font-family:Arial,sans-serif; font-weight:bold');
1249
+ return style.join('');
1250
+ };
1251
+
1252
+
1253
+ // Export Symbols for Closure
1254
+ // If you are not going to compile with closure then you can remove the
1255
+ // code below.
1256
+ window['MarkerClusterer'] = MarkerClusterer;
1257
+ MarkerClusterer.prototype['addMarker'] = MarkerClusterer.prototype.addMarker;
1258
+ MarkerClusterer.prototype['addMarkers'] = MarkerClusterer.prototype.addMarkers;
1259
+ MarkerClusterer.prototype['clearMarkers'] =
1260
+ MarkerClusterer.prototype.clearMarkers;
1261
+ MarkerClusterer.prototype['fitMapToMarkers'] =
1262
+ MarkerClusterer.prototype.fitMapToMarkers;
1263
+ MarkerClusterer.prototype['getCalculator'] =
1264
+ MarkerClusterer.prototype.getCalculator;
1265
+ MarkerClusterer.prototype['getGridSize'] =
1266
+ MarkerClusterer.prototype.getGridSize;
1267
+ MarkerClusterer.prototype['getExtendedBounds'] =
1268
+ MarkerClusterer.prototype.getExtendedBounds;
1269
+ MarkerClusterer.prototype['getMap'] = MarkerClusterer.prototype.getMap;
1270
+ MarkerClusterer.prototype['getMarkers'] = MarkerClusterer.prototype.getMarkers;
1271
+ MarkerClusterer.prototype['getMaxZoom'] = MarkerClusterer.prototype.getMaxZoom;
1272
+ MarkerClusterer.prototype['getStyles'] = MarkerClusterer.prototype.getStyles;
1273
+ MarkerClusterer.prototype['getTotalClusters'] =
1274
+ MarkerClusterer.prototype.getTotalClusters;
1275
+ MarkerClusterer.prototype['getTotalMarkers'] =
1276
+ MarkerClusterer.prototype.getTotalMarkers;
1277
+ MarkerClusterer.prototype['redraw'] = MarkerClusterer.prototype.redraw;
1278
+ MarkerClusterer.prototype['removeMarker'] =
1279
+ MarkerClusterer.prototype.removeMarker;
1280
+ MarkerClusterer.prototype['removeMarkers'] =
1281
+ MarkerClusterer.prototype.removeMarkers;
1282
+ MarkerClusterer.prototype['resetViewport'] =
1283
+ MarkerClusterer.prototype.resetViewport;
1284
+ MarkerClusterer.prototype['repaint'] =
1285
+ MarkerClusterer.prototype.repaint;
1286
+ MarkerClusterer.prototype['setCalculator'] =
1287
+ MarkerClusterer.prototype.setCalculator;
1288
+ MarkerClusterer.prototype['setGridSize'] =
1289
+ MarkerClusterer.prototype.setGridSize;
1290
+ MarkerClusterer.prototype['setMaxZoom'] =
1291
+ MarkerClusterer.prototype.setMaxZoom;
1292
+ MarkerClusterer.prototype['onAdd'] = MarkerClusterer.prototype.onAdd;
1293
+ MarkerClusterer.prototype['draw'] = MarkerClusterer.prototype.draw;
1294
+
1295
+ Cluster.prototype['getCenter'] = Cluster.prototype.getCenter;
1296
+ Cluster.prototype['getSize'] = Cluster.prototype.getSize;
1297
+ Cluster.prototype['getMarkers'] = Cluster.prototype.getMarkers;
1298
+
1299
+ ClusterIcon.prototype['onAdd'] = ClusterIcon.prototype.onAdd;
1300
+ ClusterIcon.prototype['draw'] = ClusterIcon.prototype.draw;
1301
+ ClusterIcon.prototype['onRemove'] = ClusterIcon.prototype.onRemove;
1302
+
1303
+ Object.keys = /*Object.keys || */function(o) {
1304
+ var result = [];
1305
+ for(var name in o) {
1306
+ if (o.hasOwnProperty(name))
1307
+ result.push(name);
1308
+ }
1309
+ return result;
1310
+ };
1311
+
1312
+ HTMLCollection.prototype.each = function (iterator, context) {
1313
+ for (var i = 0, length = this.length >>> 0; i < length; i++) {
1314
+ if (i in this) iterator.call(context, this[i], i, this);
1315
+ }
1316
+ };
js/fermopoint/storepickup.js ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var FermopointStorePickup = Class.create();
2
+ FermopointStorePickup.prototype = {
3
+
4
+ initialize: function(changeMethodUrl, searchUrl, mediaUrl) {
5
+ this.changeMethodUrl = changeMethodUrl;
6
+ this.searchUrl = searchUrl;
7
+ this.mediaUrl = mediaUrl;
8
+
9
+ this.error = false;
10
+ this.points = [];
11
+ this.markers = [];
12
+ this.location = null;
13
+ this.map = null;
14
+ this.clusterer = null;
15
+ this.infoWindow = null;
16
+
17
+ this.onInit = null;
18
+ this.onSearchStart = null;
19
+ this.onSearchEnd = null;
20
+ this.onSelectPoint = null;
21
+
22
+ this.setUpHook();
23
+ },
24
+
25
+ setUpHook: function () {
26
+ var fallbackValidate = ShippingMethod.prototype.validate,
27
+ fallbackNextStep = ShippingMethod.prototype.nextStep;
28
+ ShippingMethod.prototype.validate = function () {
29
+ var result,
30
+ pointId,
31
+ methods;
32
+
33
+ result = fallbackValidate.call(this);
34
+ if ( ! result)
35
+ return false;
36
+
37
+ methods = document.getElementsByName('shipping_method');
38
+ for (var i=0; i<methods.length; i++) {
39
+ if (methods[i].checked && methods[i].value !== 'fpstorepickup_fpstorepickup') {
40
+ return true;
41
+ }
42
+ }
43
+
44
+ /*if ($('fermopoint_accept_terms') && $('fermopoint_accept_terms').visible() && ! $('fermopoint_accept_terms').checked) {
45
+ alert(Translator.translate('You should accept FermoPoint terms and conditions').stripTags());
46
+ return false;
47
+ }*/
48
+ pointId = parseInt($('fermopoint_point_id').value, 10);
49
+ if (pointId <= 0) {
50
+ alert(Translator.translate('You should select one of available pick-up points to continue').stripTags());
51
+ return false;
52
+ }
53
+
54
+ return true;
55
+ };
56
+
57
+ ShippingMethod.prototype.nextStep = function (transport) {
58
+ fallbackNextStep.call(this, transport);
59
+ checkout.reloadProgressBlock('shipping');
60
+ };
61
+ },
62
+
63
+ rebound: function () {
64
+ var i,
65
+ bounds;
66
+
67
+ if ( ! this.markers.length)
68
+ return;
69
+
70
+ bounds = new google.maps.LatLngBounds();
71
+
72
+ if (this.location)
73
+ bounds.extend(this.location.getPosition());
74
+
75
+ for (i = 0; i < this.markers.length; i++)
76
+ bounds.extend(this.markers[i].getPosition());
77
+
78
+ this.map.fitBounds(bounds);
79
+ },
80
+
81
+ showPointInfo: function (marker, idx) {
82
+ var point = this.points[idx],
83
+ days = [];
84
+ for (var i = 0; i < point.hours.length; i++) {
85
+ days.push('<span class="dow">' + point.hours[i].day + '</span>' + point.hours[i].hours.join(', '));
86
+ }
87
+ this.infoWindow.setContent(
88
+ '<div class="fermopoint-info-window">' +
89
+ '<div class="row title">' + point.name + '</div>' +
90
+ '<div class="row"></div>' +
91
+ '<div class="row distance"><strong>' + Translator.translate('Distance') + ': </strong>' + point.distance + ' km </div>' +
92
+ '<div class="row contact"><strong>' + Translator.translate('Contact') + ': </strong>' + point.contact + '</div>'+
93
+ '<div class="row category"><strong>' + Translator.translate('Category') + ': </strong>' + point.category + '</div>'+
94
+ '<div class="row hours"><!--strong>' + Translator.translate('Hours') + ': </strong--><div class="hours-list">' + days.join('<br />') + '</div></div>'+
95
+ '<div class="row select"><a class="fermopoint-select-me" rel="' + idx + '" href="#">' + Translator.translate('Select this pick-up point') + '</a></div>'
96
+ );
97
+ this.infoWindow.open(this.map, marker);
98
+ },
99
+
100
+ setPoints: function (points) {
101
+ var i, marker, point, caller = this;
102
+ for (i = 0; i < this.markers.length; i++)
103
+ this.markers[i].setMap(null);
104
+
105
+ this.clusterer.clearMarkers();
106
+ this.markers.length = 0;
107
+
108
+ this.points = points;
109
+ if ( ! this.points.length)
110
+ return;
111
+
112
+ for (i = 0; i < this.points.length; i++) {
113
+ point = this.points[i];
114
+ marker = new google.maps.Marker({
115
+ position: new google.maps.LatLng(point.latitude, point.longitude),
116
+ map: this.map,
117
+ icon: this.mediaUrl + 'marker_point.png'
118
+ });
119
+ google.maps.event.addListener(marker, 'click', (function(marker, i) {
120
+ return function () {
121
+ caller.showPointInfo(marker, i);
122
+ }
123
+ })(marker, i));
124
+ this.markers.push(marker);
125
+ }
126
+
127
+ this.clusterer.addMarkers(this.markers);
128
+
129
+ this.rebound();
130
+ },
131
+
132
+ choosePoint: function (idx) {
133
+ this.infoWindow.close();
134
+ if (typeof(this.onSelectPoint) === 'function')
135
+ this.onSelectPoint.call(this, this.points[idx]);
136
+ },
137
+
138
+ initMap: function (container) {
139
+ var mapOptions = {
140
+ zoom: 5,
141
+ center: new google.maps.LatLng(41.9000, 12.4833)
142
+ },
143
+ caller = this;
144
+
145
+ Event.observe(container, 'click', function (event) {
146
+ var target = Event.findElement(event);
147
+
148
+ if (target.hasClassName('fermopoint-select-me')) {
149
+ Event.stop(event);
150
+ caller.choosePoint(parseInt(target.rel), 10);
151
+ }
152
+ });
153
+
154
+ this.infoWindow = new google.maps.InfoWindow();
155
+ this.map = new google.maps.Map(document.getElementById(container), mapOptions);
156
+ this.clusterer = new MarkerClusterer(this.map, this.markers, {
157
+ gridSize: 45,
158
+ styles: [{
159
+ height: 36, width: 36, textColor: "#ffffff", textSize: 11,
160
+ url: this.mediaUrl + 'cluster1.png'
161
+ }, {
162
+ height: 42, width: 42, textColor: "#ffffff", textSize: 11,
163
+ url: this.mediaUrl + 'cluster2.png'
164
+ }, {
165
+ height: 50, width: 50, textColor: "#ffffff", textSize: 11,
166
+ url: this.mediaUrl + 'cluster3.png'
167
+ }, {
168
+ height: 60, width: 60, textColor: "#ffffff", textSize: 11,
169
+ url: this.mediaUrl + 'cluster4.png'
170
+ }]
171
+ });
172
+
173
+ if (typeof(this.onInit) === 'function')
174
+ this.onInit.call(this);
175
+ },
176
+
177
+ setLocation: function (lat, lng) {
178
+ if (this.location)
179
+ this.location.setMap(null);
180
+ this.location = new google.maps.Marker({
181
+ position: new google.maps.LatLng(lat, lng),
182
+ map: this.map
183
+ });
184
+
185
+ this.rebound();
186
+ },
187
+
188
+ search: function (address, radius) {
189
+ var url = this.searchUrl,
190
+ caller = this;
191
+
192
+ if (typeof(this.onSearchStart) === 'function')
193
+ this.onSearchStart.call(this);
194
+
195
+ new Ajax.Request(url, {
196
+ parameters: {
197
+ address: address,
198
+ radius: radius
199
+ },
200
+ onComplete: function () {
201
+ if (typeof(caller.onSearchEnd) === 'function')
202
+ caller.onSearchEnd.call(caller, caller.points, caller.error);
203
+ },
204
+ onSuccess: function(transport) {
205
+ var json = transport.responseText.evalJSON();
206
+ caller.error = json.error;
207
+
208
+ if (json.error) {
209
+ setTimeout(function () {
210
+ alert(json.message ? json.message : Translator.translate('Error'));
211
+ }, 1);
212
+ return;
213
+ }
214
+
215
+ caller.setLocation(json.latitude, json.longitude);
216
+ caller.setPoints(json.points);
217
+ }
218
+ });
219
+ },
220
+
221
+ initGoogleMap: function (scriptUrl, container) {
222
+ if ( ! window.google || ! google.maps)
223
+ this.loadGoogleMap(scriptUrl, container);
224
+ else
225
+ this.initMap(container);
226
+ },
227
+
228
+ loadGoogleMap: function (scriptUrl, container) {
229
+ var script = document.createElement('script'),
230
+ functionName = 'initMap' + Math.floor(Math.random() * 1000001),
231
+ caller = this;
232
+
233
+ window[functionName] = function () {
234
+ caller.initMap(container);
235
+ }
236
+
237
+ script.type = 'text/javascript';
238
+ script.src = scriptUrl + '&callback=' + functionName;
239
+ document.body.appendChild(script);
240
+ },
241
+
242
+ setUseStorePickup: function(flag)
243
+ {
244
+ var url = this.changeMethodUrl;
245
+
246
+ if (flag)
247
+ url += 'flag/1';
248
+ else
249
+ url += 'flag/0';
250
+
251
+ var request = new Ajax.Request(url, {method: 'get', onFailure: ""});
252
+ }
253
+
254
+ }
media/fermopoint/cluster1.png ADDED
Binary file
media/fermopoint/cluster2.png ADDED
Binary file
media/fermopoint/cluster3.png ADDED
Binary file
media/fermopoint/cluster4.png ADDED
Binary file
media/fermopoint/marker_location.png ADDED
Binary file
media/fermopoint/marker_point.png ADDED
Binary file
package.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>fermopoint</name>
4
+ <version>1.0.2</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://www.opensource.org/licenses/osl-3.0.php">OSL v3.0</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Fermo!Points</summary>
10
+ <description>Module for integrating Fermo!Points collecting points system.</description>
11
+ <notes>Initial release.</notes>
12
+ <authors><author><name>Fermo!Point</name><user>fermopoint</user><email>support@fermopoint.it</email></author></authors>
13
+ <date>2015-02-05</date>
14
+ <time>07:01:02</time>
15
+ <contents><target name="magecommunity"><dir name="FermoPoint"><dir name="StorePickup"><dir><dir name="Block"><dir name="Adminhtml"><dir name="Configuration"><file name="Disabled.php" hash="27fc2af187259bddd66552c84231f5f5"/><file name="Manual.php" hash="132e7c4a9f89ad71dccb4ce1b2c4c242"/></dir><dir name="Remote"><dir name="Grid"><dir name="Renderer"><file name="Notes.php" hash="0d3427070e683a438abdabc1926cf316"/></dir></dir><file name="Grid.php" hash="9dcea09a45c0dabe2931380a20a97b9b"/></dir><file name="Remote.php" hash="71367be2fb6723af4ff91b8207209ab0"/><file name="Stats.php" hash="e5f884c631ab63546570ab35e0a8336c"/></dir><dir name="Checkout"><dir name="Billing"><file name="Js.php" hash="6be3156e2bb484852a9d5b0e9aa7eee2"/><file name="Radio.php" hash="6839fed605f95a79537e09467a578e58"/></dir><dir name="Onepage"><dir name="Payment"><file name="Methods.php" hash="aa8e5a31eb2ce85672e0f8aa290b4759"/></dir><dir name="Shipping"><dir name="Method"><file name="Available.php" hash="dc6592a6ed82b51a6eb8e44272a6248d"/></dir></dir></dir></dir><file name="Map.php" hash="e33785f62dce4e3615c33337746d5a3b"/></dir><dir name="Helper"><file name="Config.php" hash="3f822a7dbd46c366e96490f8d978d974"/><file name="Data.php" hash="404e3de5e4995e8a4d6d0f04774dcd4f"/></dir><dir name="Model"><dir name="Api"><file name="OrderCollection.php" hash="f0e4e89e60b4a383eb0d74dda0d03e08"/><file name="SearchData.php" hash="bef297301ae882661f131d55ad7add7b"/></dir><file name="Api.php" hash="8fcde9799d8583e5f41302d4741972c4"/><dir name="Carrier"><file name="Storepickup.php" hash="fe1eba536223f737c90b6ee1019d89e8"/></dir><file name="GoogleMaps.php" hash="ae72632965a383b2bde938e47ccf241f"/><file name="Observer.php" hash="cb69f4d76b7a34caea2f4707c2d6613c"/><dir name="Order"><file name="Point.php" hash="e5d033a2ee6add18f52bd0068102b22f"/></dir><file name="Point.php" hash="b5924ba282ad76dffc269b9c13b823f9"/><file name="Points.php" hash="37f236eabd20c5f91dc6958b600d5d21"/><dir name="Resource"><dir name="Order"><dir name="Point"><file name="Collection.php" hash="294427a66e6588ec4a5ef9af18eecd01"/></dir><file name="Point.php" hash="6c02668768be696fa833bab39fe3f9db"/></dir><dir name="Point"><file name="Collection.php" hash="9745e0182402f72ee43ce6cd7834afce"/></dir><file name="Point.php" hash="6b8e4a20dbda96bdf1c71bf56fcf8c31"/></dir><dir name="Source"><file name="Payment.php" hash="2a2213f3b7832f8891d147015c0361cd"/><file name="Selectorpayment.php" hash="8448ba93eb8ebe2c25eeeef100ff47f9"/><file name="State.php" hash="6a3f558509964600aa9491fbc7b5a15d"/></dir></dir><dir name="controllers"><dir name="Adminhtml"><file name="RemoteController.php" hash="eb93dc5af6db40ecb8a1d458d748bc58"/></dir><file name="IndexController.php" hash="339258a7f63260b5dfa7706058d08c5d"/></dir><dir name="etc"><file name="adminhtml.xml" hash="f790d0329024eba67a9018fd782a763a"/><file name="config.xml" hash="fc25e1e901dd65c43e14375d5219ef51"/><file name="jstranslator.xml" hash="44a3cd6cc3518a3509ecc2d2593e63ac"/><file name="system.xml" hash="a29a0e65758954b4ccb29d6e05b93ec4"/></dir><dir name="sql"><dir name="fpstorepickup_setup"><file name="mysql4-install-1.0.0.php" hash="c48472469c87f9975c4b203715e0137c"/><file name="mysql4-upgrade-1.0.0-1.0.1.php" hash="4801c11b1c8698ecbf9b4d857b055c33"/><file name="mysql4-upgrade-1.0.1-1.0.2.php" hash="21b100d87f0d3820e6317774c5ed273d"/></dir></dir></dir><file name="Exception.php" hash="5d30b0aa037248c2709775c8485d5fff"/></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="fpstorepickup.xml" hash="fa6cf08e5632dd37631229bc90ed8386"/></dir><dir name="template"><dir name="fpstorepickup"><file name="stats.phtml" hash="977cf97e8e340abdc86825c4e853195c"/></dir></dir></dir></dir></dir><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><file name="fpstorepickup.xml" hash="9a951988461226c665b439589defee66"/></dir><dir name="template"><dir name="fpstorepickup"><dir><dir name="checkout"><dir name="onepage"><dir name="billing"><file name="js.phtml" hash="0d0d4fa1a2a807ececff43ffad03e322"/><file name="radio.phtml" hash="81b42a465c90ad88a6a68042d74de09a"/></dir></dir></dir></dir><file name="map.phtml" hash="a77bcd12e5c89c5a2466a38edb31107e"/></dir></dir></dir></dir></dir></target><target name="mage"><dir name="app"><dir name="etc"><dir name="modules"><file name="FermoPoint_StorePickup.xml" hash="88569786925f04dade3952d7951f0fca"/></dir></dir></dir><dir name="js"><dir name="fermopoint"><file name="markerclusterer.js" hash="b623ac6d39ea8ed99d179db49b7b0bab"/><file name="storepickup.js" hash="71958cefed0620ef1076345761af83f9"/></dir></dir></target><target name="magelocale"><dir name="it_IT"><file name="FermoPoint_StorePickup.csv" hash="63bf9f2de47ebfbf269d024ffee8842b"/></dir></target><target name="magemedia"><dir name="fermopoint"><file name="cluster1.png" hash="545e7decba75c26883195707a1caf453"/><file name="cluster2.png" hash="1a2554261502135f8699081f7b7dfc15"/><file name="cluster3.png" hash="381dc5802ac150d868bab1edee7cba6c"/><file name="cluster4.png" hash="5fad1a5d344e178f587c759d9466a937"/><file name="marker_location.png" hash="000a6513abbe2c9683b19f49873710c5"/><file name="marker_point.png" hash="9b4dd5a4dd6ace99004ee529a9097de8"/></dir></target><target name="mageskin"><dir name="frontend"><dir name="base"><dir name="default"><dir name="fermopoint"><dir name="css"><file name="storepickup.css" hash="960e82d43838969e1b50dbe3465cdd6b"/></dir><dir name="images"><file name="opc-ajax-loader.gif" hash="e805ea7eca1f34c75ba0f93780d32d38"/></dir></dir></dir></dir></dir></target></contents>
16
+ <compatible/>
17
+ <dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies>
18
+ </package>
skin/frontend/base/default/fermopoint/css/storepickup.css ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #fermopoint_conditions iframe {
2
+ border: 0px;
3
+ margin: 20px 0;
4
+ height: 360px;
5
+ width: 100%;
6
+ }
7
+
8
+ #fermopoint_search+img{
9
+ display: none;
10
+ margin: 2px 0 0 10px;
11
+ }
12
+
13
+ #fermopoint_search_radius {
14
+ width: auto;
15
+ }
16
+
17
+ #fermopoint_map {
18
+ }
19
+
20
+ .fermopoint-address {
21
+ margin-top: 5px;
22
+ }
23
+
24
+ .fermopoint-search {
25
+ margin-top: 15px;
26
+ }
27
+
28
+ .fermopoint_google_map {
29
+ width: 100%;
30
+ height: 360px;
31
+ margin-top: 20px;
32
+ }
33
+
34
+ #fermopoint_point {
35
+ margin-top: 20px;
36
+ }
37
+
38
+ #fermopoint_search.in-progress+img{
39
+ display: inline-block;
40
+ }
41
+
42
+ .fermopoint-info-window {
43
+ padding: 5px;
44
+ font-size: 13px;
45
+ color: #555;
46
+ line-height: 18px;
47
+ font-family: arial;
48
+ min-width: 300px;
49
+ }
50
+
51
+ .fermopoint-info-window .row {
52
+ margin: 5px 0;
53
+ }
54
+
55
+ .fermopoint-info-window .row.title {
56
+ color: #457085;
57
+ text-transform: capitalize;
58
+ font-size: 16px;
59
+ margin-bottom: 15px;
60
+ }
61
+
62
+ .fermopoint-info-window .row strong {
63
+ color: grey !important;
64
+ font-weight: 400 !important;
65
+ }
66
+
67
+ .fermopoint-info-window .row a {
68
+ margin-top: 7px;
69
+ float: left;
70
+ color: #9bae1d;
71
+ font-size: 14px;
72
+ text-decoration: none;
73
+ }
74
+
75
+ .fermopoint-info-window .hours-list {
76
+ }
77
+
78
+ .fermopoint-info-window .hours-list .dow {
79
+ text-transform: capitalize;
80
+ font-weight: bold;
81
+ display: inline-block;
82
+ width: 40px;
83
+ }
84
+
85
+ #fermopoint_point .nick.info, #fermopoint_point .phone.info { display: block; font-size: 12px; color: #6B749A; }
86
+
skin/frontend/base/default/fermopoint/images/opc-ajax-loader.gif ADDED
Binary file