postmates - Version 1.3.0

Version Notes

Only works with 1.9.x series of Magento.

Download this release

Release Info

Developer Andrew Mager
Extension postmates
Version 1.3.0
Comparing to
See all releases


Version 1.3.0

Files changed (30) hide show
  1. app/code/community/Postmates/Shipping/Block/Adminhtml/Pickupaddress.php +208 -0
  2. app/code/community/Postmates/Shipping/Block/Adminhtml/Pickupaddress.php~ +173 -0
  3. app/code/community/Postmates/Shipping/Block/Order/Info.php~ +0 -0
  4. app/code/community/Postmates/Shipping/Block/Sales/Order/Info.php +120 -0
  5. app/code/community/Postmates/Shipping/Block/Sales/Order/Info.php~ +118 -0
  6. app/code/community/Postmates/Shipping/Client/BaseDao.php +41 -0
  7. app/code/community/Postmates/Shipping/Client/Client.php +205 -0
  8. app/code/community/Postmates/Shipping/Client/Dao/Delivery.php +26 -0
  9. app/code/community/Postmates/Shipping/Client/Dao/DeliveryQuote.php +28 -0
  10. app/code/community/Postmates/Shipping/Client/Dao/Error.php +16 -0
  11. app/code/community/Postmates/Shipping/Client/Dao/ListResponse.php +33 -0
  12. app/code/community/Postmates/Shipping/Client/Dao/PList.php +24 -0
  13. app/code/community/Postmates/Shipping/Client/Dao/Response.php +16 -0
  14. app/code/community/Postmates/Shipping/Client/Dao/User.php +16 -0
  15. app/code/community/Postmates/Shipping/Client/Factory.php +49 -0
  16. app/code/community/Postmates/Shipping/Model/Adminhtml/Config/Serialized.php +86 -0
  17. app/code/community/Postmates/Shipping/Model/Adminhtml/Config/Serialized.php~ +75 -0
  18. app/code/community/Postmates/Shipping/Model/Adminhtml/Emails.php +29 -0
  19. app/code/community/Postmates/Shipping/Model/Adminhtml/Emails.php~ +14 -0
  20. app/code/community/Postmates/Shipping/Model/Carrier.php +807 -0
  21. app/code/community/Postmates/Shipping/Model/Emails.php~ +0 -0
  22. app/code/community/Postmates/Shipping/Model/Observer.php +88 -0
  23. app/code/community/Postmates/Shipping/Model/Observer.php~ +48 -0
  24. app/code/community/Postmates/Shipping/design/sales/order/info.phtml +115 -0
  25. app/code/community/Postmates/Shipping/design/sales/order/info.phtml~ +103 -0
  26. app/code/community/Postmates/Shipping/docs/Postmates-Shipping-Documentation.pdf +0 -0
  27. app/code/community/Postmates/Shipping/etc/config.xml +96 -0
  28. app/code/community/Postmates/Shipping/etc/system.xml +173 -0
  29. app/code/community/Postmates/Shipping/sql/postmates_shipping_setup/install-1.0.0.php +21 -0
  30. package.xml +18 -0
app/code/community/Postmates/Shipping/Block/Adminhtml/Pickupaddress.php ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+ /**
14
+ * TODO
15
+ * - Cleanup; removal is only emptying the values of existing items rather than deleting the entire item...
16
+ * - Add attribute to indicate location is active / inactive
17
+ * @note This file adapted from
18
+ * https://raw.githubusercontent.com/OpenMage/magento-mirror/1.7.0.1/app/code/core/Mage/GoogleCheckout/Block/Adminhtml/Shipping/Merchant.php
19
+ */
20
+ class Postmates_Shipping_Block_Adminhtml_Pickupaddress
21
+ extends Mage_Adminhtml_Block_System_Config_Form_Field
22
+ {
23
+ protected $_addRowButtonHtml = array();
24
+ protected $_removeRowButtonHtml = array();
25
+
26
+ private $_addresses, $_cities, $_states, $_zips;
27
+
28
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
29
+ {
30
+ $this->setElement($element);
31
+
32
+ // Build the template from which new addresses can be added
33
+ $html = $this->_inputContainer(
34
+ '<div id="merchant_pickup_addresses_template" style="display:none">', '</div>');
35
+
36
+ // List the values of existing addresses
37
+ $that = $this;
38
+ $html .= $this->_inputContainer(
39
+ '<ul id="merchant_pickup_addresses_container">' .
40
+ $this->_getNextStepJs(),
41
+ '</ul>',
42
+ function() use ($that) {
43
+ $html = '';
44
+ $addresses = $that->_getValue('address');
45
+ if(count($addresses)) {
46
+ foreach($addresses as $i => $f) {
47
+ $html .= $that->_inputBlock($i);
48
+ }
49
+ }
50
+ return $html;
51
+ });
52
+
53
+ $html .= $this->_getAddRowButtonHtml(
54
+ 'merchant_pickup_addresses_container',
55
+ 'merchant_pickup_addresses_template',
56
+ $this->__('Add Pickup Address'));
57
+
58
+ return $html;
59
+ }
60
+
61
+ private function _getNextStepJs()
62
+ {
63
+ return
64
+ <<<JS
65
+ <script>
66
+ $$('#merchant_pickup_addresses_container .pickup_address').each(function(e) {
67
+ e.childElements().each(function(input) {
68
+ console.log(input.name);
69
+ });
70
+ });
71
+ </script>
72
+ JS;
73
+ }
74
+
75
+ private function _inputContainer($containerOpen, $containerClose, $blockCallback=null)
76
+ {
77
+ $html = $containerOpen;
78
+
79
+ if(is_callable($blockCallback)) {
80
+ $html .= $blockCallback();
81
+ } else {
82
+ $html .= $this->_inputBlock(-1);
83
+ }
84
+
85
+ return $html . $containerClose;
86
+ }
87
+
88
+ private function _inputBlock($i=0)
89
+ {
90
+ return
91
+ '<div class="pickup_address">' .
92
+ '<input type="hidden" name="pickup_address[]"/>' .
93
+ $this->_getPickupNameInputHtml($i) .
94
+ $this->_getPickupAddressInputHtml($i) .
95
+ $this->_getPickupCityInputHtml($i) .
96
+ $this->_getPickupStateInputHtml($i) .
97
+ $this->_getPickupZipInputHtml($i) .
98
+ $this->_getPickupPhoneInputHtml($i) .
99
+ $this->_getPickupBizNameInputHtml($i) .
100
+ $this->_getPickupNotesInputHtml($i) .
101
+ $this->_getRemoveRowButtonHtml() .
102
+ '</div>';
103
+ }
104
+
105
+ protected function _getPickupNameInputHtml($rowIndex = 0)
106
+ {
107
+ return $this->_getInputHtml('name', '* Name', $rowIndex, 200);
108
+ }
109
+
110
+ /**
111
+ * Retrieve html template for pickup address row
112
+ *
113
+ * @param int $rowIndex
114
+ * @return string
115
+ */
116
+ protected function _getPickupAddressInputHtml($rowIndex = 0)
117
+ {
118
+ return $this->_getInputHtml('address', '* Address', $rowIndex, 200);
119
+ }
120
+
121
+ protected function _getPickupCityInputHtml($rowIndex=0)
122
+ {
123
+ return $this->_getInputHtml('city', '* City', $rowIndex, 50);
124
+ }
125
+
126
+ protected function _getPickupStateInputHtml($rowIndex=0)
127
+ {
128
+ return $this->_getInputHtml('state', '* State', $rowIndex, 50);
129
+ }
130
+
131
+ protected function _getPickupZipInputHtml($rowIndex=0)
132
+ {
133
+ return $this->_getInputHtml('zip', '* Zip', $rowIndex, 50);
134
+ }
135
+
136
+ protected function _getPickupPhoneInputHtml($rowIndex=0)
137
+ {
138
+ return $this->_getInputHtml('phone', '* Phone', $rowIndex, 50);
139
+ }
140
+
141
+ protected function _getPickupBizNameInputHtml($rowIndex=0)
142
+ {
143
+ return $this->_getInputHtml('business', 'Business', $rowIndex, 50);
144
+ }
145
+
146
+ protected function _getPickupNotesInputHtml($rowIndex=0)
147
+ {
148
+ return $this->_getInputHtml('notes', 'Pickup Notes', $rowIndex, 50);
149
+ }
150
+
151
+ private function _getInputHtml($field, $label, $index=0, $width=50)
152
+ {
153
+ $value = '';
154
+ if($index >= 0) {
155
+ $value = $this->_getValue($field . '/' . $index);
156
+ }
157
+
158
+ $html = '<li>';
159
+ $html .= '<div style="margin:5px 0 10px;">';
160
+ $html .= '<label style="width:50px;">' . $this->__($label . ':') . '</label> ';
161
+ $html .= '<input class="input-text postmates-input" style="width:100%;" name="'
162
+ . $this->getElement()->getName() . '[' . $field . '][]" value="'
163
+ . $value . '" ' . $this->_getDisabled() . '/> ';
164
+
165
+ $html .= '</div>';
166
+ $html .= '</li>';
167
+
168
+ return $html;
169
+ }
170
+
171
+ protected function _getDisabled()
172
+ {
173
+ return $this->getElement()->getDisabled() ? ' disabled' : '';
174
+ }
175
+
176
+ protected function _getValue($key)
177
+ {
178
+ return $this->getElement()->getData('value/' . $key);
179
+ }
180
+
181
+ protected function _getAddRowButtonHtml($container, $template, $title='Add')
182
+ {
183
+ if (!isset($this->_addRowButtonHtml[$container])) {
184
+ $this->_addRowButtonHtml[$container] = $this->getLayout()->createBlock('adminhtml/widget_button')
185
+ ->setType('button')
186
+ ->setClass('add ' . $this->_getDisabled())
187
+ ->setLabel($this->__($title))
188
+ ->setOnClick("Element.insert($('" . $container . "'), {bottom: $('" . $template . "').innerHTML})")
189
+ ->setDisabled($this->_getDisabled())
190
+ ->toHtml();
191
+ }
192
+ return $this->_addRowButtonHtml[$container];
193
+ }
194
+
195
+ protected function _getRemoveRowButtonHtml($selector = 'div', $title = 'Remove')
196
+ {
197
+ if (!$this->_removeRowButtonHtml) {
198
+ $this->_removeRowButtonHtml = $this->getLayout()->createBlock('adminhtml/widget_button')
199
+ ->setType('button')
200
+ ->setClass('delete v-middle ' . $this->_getDisabled())
201
+ ->setLabel($this->__($title))
202
+ ->setOnClick("Element.remove($(this).up('" . $selector . "'))")
203
+ ->setDisabled($this->_getDisabled())
204
+ ->toHtml();
205
+ }
206
+ return $this->_removeRowButtonHtml;
207
+ }
208
+ }
app/code/community/Postmates/Shipping/Block/Adminhtml/Pickupaddress.php~ ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * TODO
4
+ * - Cleanup; removal is only emptying the values of existing items rather than deleting the entire item...
5
+ * - Add attribute to indicate location is active / inactive
6
+ * @note This file adapted from
7
+ * https://raw.githubusercontent.com/OpenMage/magento-mirror/1.7.0.1/app/code/core/Mage/GoogleCheckout/Block/Adminhtml/Shipping/Merchant.php
8
+ */
9
+ class Postmates_Shipping_Block_Adminhtml_Pickupaddress
10
+ extends Mage_Adminhtml_Block_System_Config_Form_Field
11
+ {
12
+ protected $_addRowButtonHtml = array();
13
+ protected $_removeRowButtonHtml = array();
14
+
15
+ private $_addresses, $_cities, $_states, $_zips;
16
+
17
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
18
+ {
19
+ $this->setElement($element);
20
+
21
+ // Build the template from which new addresses can be added
22
+ $html = $this->_inputContainer(
23
+ '<div id="merchant_pickup_addresses_template" style="display:none">', '</div>');
24
+
25
+ // List the values of existing addresses
26
+ $that = $this;
27
+ $html .= $this->_inputContainer(
28
+ '<ul id="merchant_pickup_addresses_container">' .
29
+ $this->_getNextStepJs(),
30
+ '</ul>',
31
+ function() use ($that) {
32
+ $html = '';
33
+ $addresses = $that->_getValue('address');
34
+ if(count($addresses)) {
35
+ foreach($addresses as $i => $f) {
36
+ $html .= $that->_inputBlock($i);
37
+ }
38
+ }
39
+ return $html;
40
+ });
41
+
42
+ $html .= $this->_getAddRowButtonHtml(
43
+ 'merchant_pickup_addresses_container',
44
+ 'merchant_pickup_addresses_template',
45
+ $this->__('Add Pickup Address'));
46
+
47
+ return $html;
48
+ }
49
+
50
+ private function _getNextStepJs()
51
+ {
52
+ return
53
+ <<<JS
54
+ <script>
55
+ $$('#merchant_pickup_addresses_container .pickup_address').each(function(e) {
56
+ e.childElements().each(function(input) {
57
+ console.log(input.name);
58
+ });
59
+ });
60
+ </script>
61
+ JS;
62
+ }
63
+
64
+ private function _inputContainer($containerOpen, $containerClose, $blockCallback=null)
65
+ {
66
+ $html = $containerOpen;
67
+
68
+ if(is_callable($blockCallback)) {
69
+ $html .= $blockCallback();
70
+ } else {
71
+ $html .= $this->_inputBlock(-1);
72
+ }
73
+
74
+ return $html . $containerClose;
75
+ }
76
+
77
+ private function _inputBlock($i=0)
78
+ {
79
+ return
80
+ '<div class="pickup_address">' .
81
+ '<input type="hidden" name="pickup_address[]"/>' .
82
+ $this->_getPickupAddressInputHtml($i) .
83
+ $this->_getPickupCityInputHtml($i) .
84
+ $this->_getPickupStateInputHtml($i) .
85
+ $this->_getPickupZipInputHtml($i) .
86
+ $this->_getRemoveRowButtonHtml() .
87
+ '</div>';
88
+ }
89
+
90
+ /**
91
+ * Retrieve html template for pickup address row
92
+ *
93
+ * @param int $rowIndex
94
+ * @return string
95
+ */
96
+ protected function _getPickupAddressInputHtml($rowIndex = 0)
97
+ {
98
+ return $this->_getInputHtml('address', 'Address', $rowIndex, 200);
99
+ }
100
+
101
+ protected function _getPickupCityInputHtml($rowIndex=0)
102
+ {
103
+ return $this->_getInputHtml('city', 'City', $rowIndex, 50);
104
+ }
105
+
106
+ protected function _getPickupStateInputHtml($rowIndex=0)
107
+ {
108
+ return $this->_getInputHtml('state', 'State', $rowIndex, 50);
109
+ }
110
+
111
+ protected function _getPickupZipInputHtml($rowIndex=0)
112
+ {
113
+ return $this->_getInputHtml('zip', 'Zip', $rowIndex, 50);
114
+ }
115
+
116
+ private function _getInputHtml($field, $label, $index=0, $width=50)
117
+ {
118
+ $value = '';
119
+ if($index >= 0) {
120
+ $value = $this->_getValue($field . '/' . $index);
121
+ }
122
+
123
+ $html = '<li>';
124
+ $html .= '<div style="margin:5px 0 10px;">';
125
+ $html .= '<label style="width:50px;">' . $this->__($label . ':') . '</label> ';
126
+ $html .= '<input class="input-text postmates-input" style="width:100%;" name="'
127
+ . $this->getElement()->getName() . '[' . $field . '][]" value="'
128
+ . $value . '" ' . $this->_getDisabled() . '/> ';
129
+
130
+ $html .= '</div>';
131
+ $html .= '</li>';
132
+
133
+ return $html;
134
+ }
135
+
136
+ protected function _getDisabled()
137
+ {
138
+ return $this->getElement()->getDisabled() ? ' disabled' : '';
139
+ }
140
+
141
+ protected function _getValue($key)
142
+ {
143
+ return $this->getElement()->getData('value/' . $key);
144
+ }
145
+
146
+ protected function _getAddRowButtonHtml($container, $template, $title='Add')
147
+ {
148
+ if (!isset($this->_addRowButtonHtml[$container])) {
149
+ $this->_addRowButtonHtml[$container] = $this->getLayout()->createBlock('adminhtml/widget_button')
150
+ ->setType('button')
151
+ ->setClass('add ' . $this->_getDisabled())
152
+ ->setLabel($this->__($title))
153
+ ->setOnClick("Element.insert($('" . $container . "'), {bottom: $('" . $template . "').innerHTML})")
154
+ ->setDisabled($this->_getDisabled())
155
+ ->toHtml();
156
+ }
157
+ return $this->_addRowButtonHtml[$container];
158
+ }
159
+
160
+ protected function _getRemoveRowButtonHtml($selector = 'div', $title = 'Remove')
161
+ {
162
+ if (!$this->_removeRowButtonHtml) {
163
+ $this->_removeRowButtonHtml = $this->getLayout()->createBlock('adminhtml/widget_button')
164
+ ->setType('button')
165
+ ->setClass('delete v-middle ' . $this->_getDisabled())
166
+ ->setLabel($this->__($title))
167
+ ->setOnClick("Element.remove($(this).up('" . $selector . "'))")
168
+ ->setDisabled($this->_getDisabled())
169
+ ->toHtml();
170
+ }
171
+ return $this->_removeRowButtonHtml;
172
+ }
173
+ }
app/code/community/Postmates/Shipping/Block/Order/Info.php~ ADDED
File without changes
app/code/community/Postmates/Shipping/Block/Sales/Order/Info.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+ /**
14
+ * Relegate the design directory to our module.
15
+ */
16
+ class Postmates_Shipping_Block_Sales_Order_Info extends Mage_Sales_Block_Order_Info
17
+ {
18
+ /**
19
+ * Fetch a status of the delivery from Postmates to pass to the template.
20
+ */
21
+ protected function _construct()
22
+ {
23
+ parent::_construct();
24
+
25
+ try {
26
+ // Lookup the Postmates delivery ID via the Magento order
27
+ $order = $this->getOrder();
28
+ $oPostmatesCarrier = Mage::getModel('postmates_shipping/carrier');
29
+ $sPostmatesDeliveryId = $order->getPostmatesDeliveryId();
30
+
31
+ // If the order has no Postmates delivery ID, display the error status and bail
32
+ if(empty($sPostmatesDeliveryId)) {
33
+ $this->_setErrorStatus();
34
+ return;
35
+ }
36
+
37
+ // Get the status from Postmates
38
+ $oStatus = $oPostmatesCarrier->getStatus($sPostmatesDeliveryId);
39
+
40
+ // Pass an appropriate message to the template
41
+ if($oStatus->isError()) {
42
+ $this->_setErrorStatus();
43
+ } else {
44
+ switch($oStatus['status']) {
45
+ // @note These default messages are taken directly from the Postmates API documentation
46
+ // https://postmates.com/developer/docs/endpoints#list_deliveries
47
+ case Postmates_Shipping_Client_Client::STATUS_PENDING:
48
+ $this->setDeliveryStatus('Delivery accepted, a courier will be assigned soon');
49
+ break;
50
+ case Postmates_Shipping_Client_Client::STATUS_PICKUP:
51
+ $this->setDeliveryStatus('Courier is assigned and is en route to pick up your item(s)');
52
+ break;
53
+ case Postmates_Shipping_Client_Client::STATUS_PICKUP_COMPLETE:
54
+ $this->setDeliveryStatus('Courier has picked up your item(s)');
55
+ break;
56
+ case Postmates_Shipping_Client_Client::STATUS_DROPOFF:
57
+ $this->setDeliveryStatus('Courier is en route to deliver your item(s)');
58
+ break;
59
+ case Postmates_Shipping_Client_Client::STATUS_CANCELED:
60
+ $this->setDeliveryStatus("Items won't be delivered. " .
61
+ 'Deliveries are either canceled by the customer or by our customer service team.');
62
+ break;
63
+ case Postmates_Shipping_Client_Client::STATUS_DELIVERED:
64
+ $this->setDeliveryStatus('Your item(s) have been delivered successfully');
65
+ break;
66
+ case Postmates_Shipping_Client_Client::STATUS_RETURNED:
67
+ $this->setDeliveryStatus(
68
+ 'The delivery was canceled and a new job created to return items to sender.');
69
+ break;
70
+ default:
71
+ $this->_setErrorStatus();
72
+ }
73
+ }
74
+ } catch(Exception $e) {
75
+ Mage::logException($e);
76
+ $this->_setErrorStatus();
77
+ }
78
+ }
79
+
80
+ /**
81
+ * For some reason we're unable to determine the status of a Postmates delivery.
82
+ * This will display a general error message to the customer.
83
+ */
84
+ protected function _setErrorStatus()
85
+ {
86
+ $this->setDeliveryStatus(
87
+ 'Unable to determine the status of your delivery. ' .
88
+ 'Please contact customer service for assistance.');
89
+ }
90
+
91
+ // Relegate template files to our extension's directory structure.
92
+ // @note The below technique borrowed from Alan Storm's e-book
93
+ // No Frills Magento Layout
94
+
95
+ /**
96
+ * Set template location directory
97
+ *
98
+ * @param string $dir
99
+ * @return Mage_Core_Block_Template
100
+ */
101
+ public function setScriptPath($dir)
102
+ {
103
+ parent::setScriptPath($dir);
104
+ $this->_viewDir = Mage::getModuleDir('', 'Postmates_Shipping') . DS . 'design';
105
+ return $this;
106
+ }
107
+
108
+ /**
109
+ * Retrieve block view from file (template)
110
+ * Ignores file name, just uses a simple include with template name.
111
+ *
112
+ * @param string $fileName
113
+ * @return string
114
+ */
115
+ public function fetchView($fileName)
116
+ {
117
+ $this->setScriptPath(Mage::getModuleDir('', 'Postmates_Shipping') . DS . 'design');
118
+ return parent::fetchView($this->getTemplate());
119
+ }
120
+ }
app/code/community/Postmates/Shipping/Block/Sales/Order/Info.php~ ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+ /**
14
+ * Relegate the design directory to our module.
15
+ */
16
+ class Postmates_Shipping_Sales_Block_Order_Info extends Mage_Sales_Block_Order_Info
17
+ {
18
+ /**
19
+ * Fetch a status of the delivery from Postmates to pass to the template.
20
+ */
21
+ protected function _construct()
22
+ {
23
+ try {
24
+ // Lookup the Postmates delivery ID via the Magento order
25
+ $order = $this->getOrder();
26
+ $oPostmatesCarrier = Mage::getModel('postmates_shipping/carrier');
27
+ $sPostmatesDeliveryId = $order->getPostmatesDeliveryId();
28
+
29
+ // If the order has no Postmates delivery ID, display the error status and bail
30
+ if(empty($sPostmatesDeliveryId)) {
31
+ $this->_setErrorStatus();
32
+ return;
33
+ }
34
+
35
+ // Get the status from Postmates
36
+ $oStatus = $oPostmatesCarrier->getStatus($sPostmatesDeliveryId);
37
+
38
+ // Pass an appropriate message to the template
39
+ if($oStatus->isError()) {
40
+ $this->_setErrorStatus();
41
+ } else {
42
+ switch($oStatus['status']) {
43
+ // @note These default messages are taken directly from the Postmates API documentation
44
+ // https://postmates.com/developer/docs/endpoints#list_deliveries
45
+ case Postmates_Shipping_Client_Client::STATUS_PENDING:
46
+ $this->setDeliveryStatus('Delivery accepted, a courier will be assigned soon');
47
+ break;
48
+ case Postmates_Shipping_Client_Client::STATUS_PICKUP:
49
+ $this->setDeliveryStatus('Courier is assigned and is en route to pick up your item(s)');
50
+ break;
51
+ case Postmates_Shipping_Client_Client::STATUS_PICKUP_COMPLETE:
52
+ $this->setDeliveryStatus('Courier has picked up your item(s)');
53
+ break;
54
+ case Postmates_Shipping_Client_Client::STATUS_DROPOFF:
55
+ $this->setDeliveryStatus('Courier is en route to deliver your item(s)');
56
+ break;
57
+ case Postmates_Shipping_Client_Client::STATUS_CANCELED:
58
+ $this->setDeliveryStatus("Items won't be delivered. " .
59
+ 'Deliveries are either canceled by the customer or by our customer service team.');
60
+ break;
61
+ case Postmates_Shipping_Client_Client::STATUS_DELIVERED:
62
+ $this->setDeliveryStatus('Your item(s) have been delivered successfully');
63
+ break;
64
+ case Postmates_Shipping_Client_Client::STATUS_RETURNED:
65
+ $this->setDeliveryStatus(
66
+ 'The delivery was canceled and a new job created to return items to sender.');
67
+ break;
68
+ default:
69
+ $this->_setErrorStatus();
70
+ }
71
+ }
72
+ } catch(Exception $e) {
73
+ Mage::logException($e);
74
+ $this->_setErrorStatus();
75
+ }
76
+ }
77
+
78
+ /**
79
+ * For some reason we're unable to determine the status of a Postmates delivery.
80
+ * This will display a general error message to the customer.
81
+ */
82
+ protected function _setErrorStatus()
83
+ {
84
+ $this->setDeliveryStatus(
85
+ 'Unable to determine the status of your delivery. ' .
86
+ 'Please contact customer service for assistance.');
87
+ }
88
+
89
+
90
+ // @note The below technique borrowed from Alan Storm's e-book
91
+ // No Frills Magento Layout
92
+
93
+ /**
94
+ * Set template location directory
95
+ *
96
+ * @param string $dir
97
+ * @return Mage_Core_Block_Template
98
+ */
99
+ public function setScriptPath($dir)
100
+ {
101
+ parent::setScriptPath($dir);
102
+ $this->_viewDir = Mage::getModuleDir('', 'Postmates_Shipping') . DS . 'design';
103
+ return $this;
104
+ }
105
+
106
+ /**
107
+ * Retrieve block view from file (template)
108
+ * Ignores file name, just uses a simple include with template name.
109
+ *
110
+ * @param string $fileName
111
+ * @return string
112
+ */
113
+ public function fetchView($fileName)
114
+ {
115
+ $this->setScriptPath(Mage::getModuleDir('', 'Postmates_Shipping') . DS . 'design');
116
+ return parent::fetchView($this->getTemplate());
117
+ }
118
+ }
app/code/community/Postmates/Shipping/Client/BaseDao.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+ class Postmates_Shipping_Client_BaseDao extends \ArrayObject
14
+ {
15
+ public function __construct($input=array(), $flags=0, $iterator_class='ArrayIterator')
16
+ {
17
+ // Give the child a chance to map any of the values
18
+ $mapped = $this->_map($input);
19
+ parent::__construct($mapped, $flags, $iterator_class);
20
+ }
21
+
22
+ /**
23
+ * Override to customize the $input before an ArrayObject is created.
24
+ */
25
+ protected function _map(array $input)
26
+ {
27
+ return $input;
28
+ }
29
+
30
+ /**
31
+ * Postmates dates are all ISO8601 formatted.
32
+ * This is a convenience method to hydrate a DateTime object for
33
+ * a given string represenation in an API response.
34
+ */
35
+ static public function mapDateTime($sDate)
36
+ {
37
+ return date_create($sDate);
38
+ }
39
+
40
+ public function isError() { return false; }
41
+ }
app/code/community/Postmates/Shipping/Client/Client.php ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+ class Postmates_Shipping_Client_Client extends Zend_Http_Client
14
+ {
15
+ const STATUS_PENDING = 'pending'; // We've accepted the delivery and will be assigning it to a courier.
16
+ const STATUS_PICKUP = 'pickup'; // Courier is assigned and is en route to pick up the items
17
+ const STATUS_PICKUP_COMPLETE = 'pickup_complete'; // Courier has picked up the items
18
+ const STATUS_DROPOFF = 'dropoff'; // Courier is moving towards the dropoff
19
+ const STATUS_CANCELED = 'canceled'; // Items won't be delivered. Deliveries are either canceled by the customer or by our customer service team.
20
+ const STATUS_DELIVERED = 'delivered'; // Items were delivered successfully.
21
+ const STATUS_RETURNED = 'returned'; // The delivery was canceled and a new job created to return items to sender. (See related_deliveries in delivery object.)
22
+
23
+ const API_VERSION = 'v1';
24
+
25
+ static private $_aValidStatuses = array(
26
+ self::STATUS_PENDING, self::STATUS_PICKUP, self::STATUS_PICKUP_COMPLETE, self::STATUS_DROPOFF,
27
+ self::STATUS_CANCELED, self::STATUS_DELIVERED, self::STATUS_RETURNED
28
+ );
29
+
30
+ private $_sCustomerId;
31
+
32
+ public function __construct($sUri='', array $config=array())
33
+ {
34
+ // Validate Postmates config values, these are required for the Postmates Client
35
+ if(!isset($config['customer_id']))
36
+ throw new \InvalidArgumentException('Missing the Postmates Customer ID');
37
+ if(!isset($config['api_key']))
38
+ throw new \InvalidArgumentException('Missing the Postmates API Key');
39
+
40
+ // Construct the underlying Zend_Http_Client
41
+ parent::__construct();
42
+
43
+ // Store the customer id on the instance for URI generation
44
+ $this->_sCustomerId = $config['customer_id'];
45
+
46
+ // Optional Postmates version
47
+ $aHeaders = array();
48
+ if(isset($config['postmates_version']))
49
+ $this->setHeaders('X-Postmates-Version', $config['postmates_version']);
50
+
51
+ // HTTP Basic auth header, username is api key, password is blank
52
+ $this->setAuth($config['api_key'], '');
53
+ }
54
+
55
+ /**
56
+ * The first step in using the Postmates API is get a quote on a delivery.
57
+ * This allows you to make decisions about the appropriate cost and availability
58
+ * for using the postmates platform, which can vary based on distance and demand.
59
+ *
60
+ * A Delivery Quote is only valid for a limited duration. After which, referencing
61
+ * the quote while creating a delivery will not be allowed.
62
+ *
63
+ * You'll receive a DeliveryQuote response.
64
+ */
65
+ public function requestDeliveryQuote($sPickupAddress, $sDropoffAddress)
66
+ {
67
+ return $this->_postRequest(
68
+ "/customers/{$this->_sCustomerId}/delivery_quotes",
69
+ array('pickup_address' => $sPickupAddress, 'dropoff_address' => $sDropoffAddress));
70
+ }
71
+
72
+ /**
73
+ * Once a delivery is accepted, the delivery fee will be deducted from your account.
74
+ * Providing the previously generated quote id is optional, but ensures the costs
75
+ * and etas are consistent with the quote.
76
+ */
77
+ public function createDelivery(
78
+ $sManifest,
79
+ $sPickupName,
80
+ $sPickupAddress,
81
+ $sPickupPhoneNumber,
82
+ $sDropoffName,
83
+ $sDropoffAddress,
84
+ $sDropoffPhoneNumber,
85
+ $sDropoffBusinessName='',
86
+ $sManifestReference='',
87
+ $sPickupBusinessName='',
88
+ $sPickupNotes='',
89
+ $sDropoffNotes='',
90
+ $iQuoteId=null
91
+ ) {
92
+ // Add the required arguments
93
+ $aParams = array(
94
+ 'manifest' => $sManifest,
95
+ 'pickup_name' => $sPickupName,
96
+ 'pickup_address' => $sPickupAddress,
97
+ 'pickup_phone_number' => $sPickupPhoneNumber,
98
+ 'dropoff_name' => $sDropoffName,
99
+ 'dropoff_address' => $sDropoffAddress,
100
+ 'dropoff_phone_number' => $sDropoffPhoneNumber,
101
+ );
102
+
103
+ // Add optional arguments
104
+ if(!empty($sDropffBusinessName))
105
+ $aParams['dropoff_business_name'] = $sDropoffBusinessName;
106
+ if(!empty($sManifestReference))
107
+ $aParams['manifest_reference'] = $sManifestReference;
108
+ if(!empty($sPickupBusinessName))
109
+ $aParams['pickup_business_name'] = $sPickupBusinessName;
110
+ if(!empty($sPickupNotes))
111
+ $aParams['pickup_notes'] = $sPickupNotes;
112
+ if($iQuoteId !== null)
113
+ $aParams['quote_id'] = $iQuoteId;
114
+
115
+ // Configure and send the request
116
+ return $this->_postRequest("/customers/{$this->_sCustomerId}/deliveries", $aParams);
117
+ }
118
+
119
+ /**
120
+ * List all deliveries for a customer optionally restricted by a provided status.
121
+ */
122
+ public function listDeliveries($sStatusFilter='')
123
+ {
124
+ $aOptions = array();
125
+ if($sStatusFilter != '' && in_array($sStatusFilter, self::$_aValidStatuses))
126
+ $aOptions['filter'] = $sStatusFilter;
127
+
128
+ return $this->_getRequest(
129
+ "/customers/{$this->_sCustomerId}/deliveries",
130
+ $aOptions);
131
+ }
132
+
133
+ /**
134
+ * Retrieve updated details about a delivery.
135
+ * Returns: Delivery Object
136
+ */
137
+ public function getDeliveryStatus($iDeliveryId)
138
+ {
139
+ return $this->_getRequest("/customers/{$this->_sCustomerId}/deliveries/{$iDeliveryId}");
140
+ }
141
+
142
+ /**
143
+ * Cancel an ongoing delivery.
144
+ * Returns: Delivery Object
145
+ * A delivery can only be canceled prior to a courier completing pickup. Delivery fees still apply.
146
+ */
147
+ public function cancelDelivery($iDeliveryId)
148
+ {
149
+ return $this->_postRequest("/customers/{$this->_sCustomerId}/deliveries/{$iDeliveryId}/cancel");
150
+ }
151
+
152
+ /**
153
+ * Cancel an ongoing delivery that was already picked up
154
+ * and create a delivery that is a reverse of the original.
155
+ * The items will get returned to the original pickup location.
156
+ *
157
+ * A delivery can only be reversed once the courier completed pickup and before the
158
+ * courier has completed dropoff. Delivery fees apply to both the cancelled delivery
159
+ * and new returned delivery.
160
+ *
161
+ * Returns: Delivery Object (the new return delivery)
162
+ */
163
+ public function returnDelivery($iDeliveryId)
164
+ {
165
+ $oRq = $this->createRequest('POST', "customers/{$this->_sCustomerId}/deliveries/{$iDeliveryId}/return");
166
+ return $this->_request($oRq);
167
+ }
168
+
169
+
170
+ private function _setUri($sUri)
171
+ {
172
+ $this->setUri('https://api.postmates.com/' . self::API_VERSION . $sUri);
173
+ }
174
+
175
+ private function _getRequest($sUri, array $aParams=array())
176
+ {
177
+ $this->_setUri($sUri);
178
+
179
+ if(count($aParams))
180
+ $this->setParameterGet($aParams);
181
+
182
+ return $this->_request('GET');
183
+ }
184
+
185
+ private function _postRequest($sUri, array $aParams=array())
186
+ {
187
+ $this->_setUri($sUri);
188
+
189
+ if(count($aParams))
190
+ $this->setParameterPost($aParams);
191
+
192
+ return $this->_request('POST');
193
+ }
194
+
195
+ /**
196
+ * Make the API request and hydrates Dao(s) from the ressponse data.
197
+ */
198
+ private function _request($sMethod)
199
+ {
200
+ $oRsp = $this->request($sMethod);
201
+ $aData = Zend_Json::decode($oRsp->getBody());
202
+
203
+ return Postmates_Shipping_Client_Factory::create($aData);
204
+ }
205
+ }
app/code/community/Postmates/Shipping/Client/Dao/Delivery.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+ class Postmates_Shipping_Client_Dao_Delivery extends Postmates_Shipping_Client_BaseDao
14
+ {
15
+ protected function _map(array $input)
16
+ {
17
+ // Map raw date times to objects
18
+ $input['created'] = self::mapDateTime($input['created']);
19
+ $input['updated'] = self::mapDateTime($input['updated']);
20
+ $input['pickup_eta'] = self::mapDateTime($input['pickup_eta']);
21
+ $input['dropoff_eta'] = self::mapDateTime($input['dropoff_eta']);
22
+ $input['dropoff_deadline'] = self::mapDateTime($input['dropoff_deadline']);
23
+
24
+ return $input;
25
+ }
26
+ }
app/code/community/Postmates/Shipping/Client/Dao/DeliveryQuote.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+ class Postmates_Shipping_Client_Dao_DeliveryQuote extends Postmates_Shipping_Client_BaseDao
14
+ {
15
+ protected function _map(array $input)
16
+ {
17
+ // Map raw date times to objects
18
+ $input['created'] = self::mapDateTime($input['created']);
19
+ $input['expires'] = self::mapDateTime($input['expires']);
20
+ $input['dropoff_eta'] = self::mapDateTime($input['dropoff_eta']);
21
+
22
+ // Postmates passes the fee in cents
23
+ $input['fee'] = $input['fee'] / 100;
24
+
25
+ return $input;
26
+ }
27
+ }
28
+
app/code/community/Postmates/Shipping/Client/Dao/Error.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+ class Postmates_Shipping_Client_Dao_Error extends Postmates_Shipping_Client_BaseDao
14
+ {
15
+ public function isError() { return true; }
16
+ }
app/code/community/Postmates/Shipping/Client/Dao/ListResponse.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+ class Postmates_Shipping_Client_ListResponse extends \ArrayObject
14
+ {
15
+ public function getTotalCount()
16
+ {
17
+ if(!isset($this['total_count']))
18
+ return 1;
19
+ return $this['total_count'];
20
+ }
21
+
22
+ public function getNextHref()
23
+ {
24
+ if(!isset($this['next_href']))
25
+ return null;
26
+ return $this['next_href'];
27
+ }
28
+
29
+ public function getData()
30
+ {
31
+ return $this['data'];
32
+ }
33
+ }
app/code/community/Postmates/Shipping/Client/Dao/PList.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+ class Postmates_Shipping_Client_Dao_PList extends Postmates_Shipping_Client_BaseDao
14
+ {
15
+ protected function _map(array $input)
16
+ {
17
+ // Map all the children in the list
18
+ $_aInput = array();
19
+ foreach($input['data'] as $_aObject)
20
+ $_aInput[] = Postmates_Shipping_Client_Factory::create($_aObject);
21
+
22
+ return $_aInput;
23
+ }
24
+ }
app/code/community/Postmates/Shipping/Client/Dao/Response.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+ class Postmates_Shipping_Client_Response extends \ArrayObject
14
+ {
15
+
16
+ }
app/code/community/Postmates/Shipping/Client/Dao/User.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+ class Postmates_Shipping_Client_Dao_User extends \ArrayObject
14
+ {
15
+
16
+ }
app/code/community/Postmates/Shipping/Client/Factory.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+ class Postmates_Shipping_Client_Factory
14
+ {
15
+ /**
16
+ * This is a factory method that will instantiate the appropriate PHP
17
+ * object by inspecting the response payload.
18
+ */
19
+ static public function create(array $aJson)
20
+ {
21
+ // Seems Postmates sometimes uses the key 'object' when
22
+ // they pass a list kind, although the docs say it will be in kind...
23
+ if(isset($aJson['object']) && $aJson['object'] == 'list')
24
+ return new Postmates_Shipping_Client_Dao_PList($aJson);
25
+
26
+ // Now try to hydrate a known object
27
+ $sKind = $aJson['kind'];
28
+ switch($sKind) {
29
+ case 'list':
30
+ return new Postmates_Shipping_Client_Dao_PList($aJson);
31
+ break;
32
+ case 'delivery_quote':
33
+ return new Postmates_Shipping_Client_Dao_DeliveryQuote($aJson);
34
+ break;
35
+ case 'delivery':
36
+ return new Postmates_Shipping_Client_Dao_Delivery($aJson);
37
+ break;
38
+ case 'error':
39
+ return new Postmates_Shipping_Client_Dao_Error($aJson);
40
+ break;
41
+ default;
42
+ throw new \UnexpectedValueException("Unsupported type $sKind");
43
+ break;
44
+ }
45
+
46
+ // If no type was provided return the bare array
47
+ return $aJson;
48
+ }
49
+ }
app/code/community/Postmates/Shipping/Model/Adminhtml/Config/Serialized.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+ class Postmates_Shipping_Model_Adminhtml_Config_Serialized
14
+ extends Mage_Core_Model_Config_Data
15
+ {
16
+ static public function unpack($serialized)
17
+ {
18
+ $objects = unserialize($serialized);
19
+ $origFormat = array();
20
+ if(count($objects)) {
21
+ foreach($objects as $object) {
22
+ foreach($object as $key => $value) {
23
+ if(!isset($origFormat[$key])) {
24
+ $origFormat[$key] = array();
25
+ }
26
+
27
+ $origFormat[$key][] = $value;
28
+ }
29
+ }
30
+ }
31
+
32
+ return $origFormat;
33
+ }
34
+
35
+ protected function _afterLoad()
36
+ {
37
+ if (!is_array($this->getValue())) {
38
+ $data = $this->getValue();
39
+
40
+ if(empty($data)) {
41
+ $this->setValue(false);
42
+ } else {
43
+ $origFormat = self::unpack($data);
44
+
45
+ if(count($origFormat)) {
46
+ $this->setValue($origFormat);
47
+ } else {
48
+ $this->setValue(false);
49
+ }
50
+ }
51
+ }
52
+ }
53
+
54
+ protected function _beforeSave()
55
+ {
56
+ if (is_array($this->getValue())) {
57
+ $value = $this->getValue();
58
+
59
+ // Aggregate the values
60
+ $value = $this->getValue();
61
+ $cleanValues = array();
62
+ foreach($value as $key => $values) {
63
+ foreach($values as $i => $_value) {
64
+ $cleanValues[$i][$key] = $_value;
65
+ }
66
+ }
67
+
68
+ // Now validate
69
+ $finalValues = array();
70
+ foreach($cleanValues as $value) {
71
+ $valid = true;
72
+ foreach($value as $key => $_value) {
73
+ if($key != 'business' && $key != 'notes' && empty($_value)) {
74
+ $valid = false;
75
+ break;
76
+ }
77
+ }
78
+ if($valid) {
79
+ $finalValues[] = $value;
80
+ }
81
+ }
82
+
83
+ $this->setValue(serialize($finalValues));
84
+ }
85
+ }
86
+ }
app/code/community/Postmates/Shipping/Model/Adminhtml/Config/Serialized.php~ ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Postmates_Shipping_Model_Adminhtml_Config_Serialized
3
+ extends Mage_Core_Model_Config_Data
4
+ {
5
+ static public function unpack($serialized)
6
+ {
7
+ $objects = unserialize($serialized);
8
+ $origFormat = [];
9
+ if(count($objects)) {
10
+ foreach($objects as $object) {
11
+ foreach($object as $key => $value) {
12
+ if(!isset($origFormat[$key])) {
13
+ $origFormat[$key] = [];
14
+ }
15
+
16
+ $origFormat[$key][] = $value;
17
+ }
18
+ }
19
+ }
20
+
21
+ return $origFormat;
22
+ }
23
+
24
+ protected function _afterLoad()
25
+ {
26
+ if (!is_array($this->getValue())) {
27
+ $data = $this->getValue();
28
+
29
+ if(empty($data)) {
30
+ $this->setValue(false);
31
+ } else {
32
+ $origFormat = self::unpack($data);
33
+
34
+ if(count($origFormat)) {
35
+ $this->setValue($origFormat);
36
+ } else {
37
+ $this->setValue(false);
38
+ }
39
+ }
40
+ }
41
+ }
42
+
43
+ protected function _beforeSave()
44
+ {
45
+ if (is_array($this->getValue())) {
46
+ $value = $this->getValue();
47
+
48
+ // Aggregate the values
49
+ $value = $this->getValue();
50
+ $cleanValues = [];
51
+ foreach($value as $key => $values) {
52
+ foreach($values as $i => $_value) {
53
+ $cleanValues[$i][$key] = $_value;
54
+ }
55
+ }
56
+
57
+ // Now validate
58
+ $finalValues = [];
59
+ foreach($cleanValues as $value) {
60
+ $valid = true;
61
+ foreach($value as $key => $_value) {
62
+ if(empty($_value)) {
63
+ $valid = false;
64
+ break;
65
+ }
66
+ }
67
+ if($valid) {
68
+ $finalValues[] = $value;
69
+ }
70
+ }
71
+
72
+ $this->setValue(serialize($finalValues));
73
+ }
74
+ }
75
+ }
app/code/community/Postmates/Shipping/Model/Adminhtml/Emails.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+ /**
14
+ * This class defines a select list where the admin can choose an email to send an
15
+ * error notification to.
16
+ */
17
+ class Postmates_Shipping_Model_Adminhtml_Emails
18
+ {
19
+ public function toOptionArray()
20
+ {
21
+ return array(
22
+ array('value' => 'general', 'label' => 'General Email'),
23
+ array('value' => 'sales', 'label' => 'Sales Email'),
24
+ array('value' => 'support', 'label' => 'Support Email'),
25
+ array('value' => 'custom1', 'label' => 'Custom1 Email'),
26
+ array('value' => 'custom2', 'label' => 'Custom2 Email'),
27
+ );
28
+ }
29
+ }
app/code/community/Postmates/Shipping/Model/Adminhtml/Emails.php~ ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Postmates_Shipping_Model_Adminhtml_Emails
3
+ {
4
+ public function toOptionArray()
5
+ {
6
+ return array(
7
+ array('value' => 'general', 'label' => 'General Email'),
8
+ array('value' => 'sales', 'label' => 'Sales Email'),
9
+ array('value' => 'support', 'label' => 'Support Email'),
10
+ array('value' => 'custom1', 'label' => 'Custom1 Email'),
11
+ array('value' => 'custom2', 'label' => 'Custom2 Email'),
12
+ );
13
+ }
14
+ }
app/code/community/Postmates/Shipping/Model/Carrier.php ADDED
@@ -0,0 +1,807 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+ /**
14
+ * Postmates will implement delivery via crowd-sourced options.
15
+ */
16
+ class Postmates_Shipping_Model_Carrier
17
+ extends Mage_Shipping_Model_Carrier_Abstract
18
+ implements Mage_Shipping_Model_Carrier_Interface
19
+ {
20
+ // @note Various attributes marked protected -
21
+ // set them yourself in a subclass only if you know what you're doing!
22
+ protected
23
+ $_code = 'postmates_shipping',
24
+ $_sApiKey = '',
25
+ $_sCustomerId = '',
26
+ $_bEstimatesEnabled = false,
27
+ $_sCustomTitle = '',
28
+ $_sCustomMethod = '',
29
+ $_fFlatRate = '',
30
+ $_bQuoteNotificationsEnabled = false,
31
+ $_sQuoteNotificationEmail = '',
32
+ $_iQuoteEmailTemplateId = -1,
33
+ $_bDeliveryNotificationsEnabled = false,
34
+ $_sDeliveryNotificationEmail = '',
35
+ $_iDeliveryEmailTemplateId = -1,
36
+ $_aPickupAddrs = array();
37
+
38
+ /* array representation of the dropoff address */
39
+ private $_aDropoffAddr;
40
+
41
+ private $_oMageSession;
42
+
43
+ public function getAllowedMethods()
44
+ {
45
+ return array('postmates' => 'Postmates Delivery');
46
+ }
47
+
48
+ /**
49
+ * Declare the carrier title and method. Note unlike other built-in carriers like UPS and USPS etc,
50
+ * there is only one method for Postmates. Admins are able to customize the carrier title and name
51
+ * of the method in the admin if they wish.
52
+ *
53
+ * This method will determine the optimal pickup address(es) if there are more than one entered in the admin
54
+ * then get quotes for all of them and present the user with the least expensive quote.
55
+ *
56
+ * This method is marked final on purpose, it's an incarnation of the template-method design pattern.
57
+ * The algorithm is defined here, various methods it relies upon that may be safely overridden have been
58
+ * marked protected.
59
+ */
60
+ final public function collectRates(Mage_Shipping_Model_Rate_Request $request)
61
+ {
62
+ //---------------------------------------------------
63
+ // Load the configuration, perform initial validation
64
+ //---------------------------------------------------
65
+ $this->_oMageSession = Mage::getSingleton('core/session');
66
+
67
+ $this->_readConfigData();
68
+
69
+ // Get the currently selected shipping address and convert it to an array which will
70
+ // make it compatible with our validation and string conversion methods
71
+ $oShipAddr = Mage::getSingleton('checkout/session')->getQuote()->getShippingAddress();
72
+
73
+ // Get the shipping address and convert it to an array for convenience
74
+ $this->_aDropoffAddr = self::_addrObjectToArray($oShipAddr);
75
+
76
+ // Bail if the shipping address is invalid
77
+ // @note We allow the address component to be empty because the
78
+ // estimation widget does not allow entry of one
79
+ if(!self::_addrValid($this->_aDropoffAddr, $this->_bEstimatesEnabled)) {
80
+ return false;
81
+ }
82
+
83
+ //--------------------
84
+ // Magento boilerplate
85
+ //--------------------
86
+ // Mage_Shipping_Model_Rate_Result
87
+ $oResult = Mage::getModel('shipping/rate_result');
88
+
89
+ /** @var Mage_Shipping_Model_Rate_Result_Method $oRate */
90
+ $oRate = Mage::getModel('shipping/rate_result_method');
91
+
92
+ $oRate->setCarrier($this->_code);
93
+
94
+ //---------------------------------
95
+ // Customize Carrier Title & Method
96
+ //---------------------------------
97
+ $this->_customizeCarrierTitle($oRate);
98
+ $this->_customizeCarrierMethod($oRate);
99
+
100
+ //-------------------------------------------------------------------
101
+ // Dynamically populate the delivery price via a quote from Postmates
102
+ // @note The first call here is overridable, so you may modify the
103
+ // logic by which the ideal pickup address is selected
104
+ //-------------------------------------------------------------------
105
+ $aIdealPickupAddresses = $this->_selectPickupAddressesToQuote();
106
+ $aQuotesAndPickupAddrs = $this->_fetchQuotes($aIdealPickupAddresses);
107
+
108
+ //-----------------------------------------------------------------
109
+ // If we fail to determine the best quote,
110
+ // then indicate to Magento not to display anything for this module
111
+ //-----------------------------------------------------------------
112
+ if(!$aQuotesAndPickupAddrs) {
113
+ Mage::log('Failed to get a quote from Postmates', Zend_Log::ERR);
114
+ return false;
115
+ }
116
+
117
+ $aQuotesAndPickupAddr = $this->_determineBestQuote($aQuotesAndPickupAddrs);
118
+ $oQuote = $aQuotesAndPickupAddr['quote'];
119
+ $fRate = $oQuote['fee'];
120
+ $aPickupAddr = $aQuotesAndPickupAddr['pickup-addr'];
121
+
122
+ if(!$oQuote || !is_array($aPickupAddr)) {
123
+ Mage::log('Failed to determine the best quote from Postmates', Zend_Log::ERR);
124
+ return false;
125
+ }
126
+
127
+ // Finally, persist the chosen Postmates quote and pickup address
128
+ $this->_persistPostmatesQuote($oQuote);
129
+ $this->_persistPickupAddr($aPickupAddr);
130
+
131
+ //------------------------------------------------------------------
132
+ // Set the price and the cost of this rate
133
+ // @note Intentionally allowing flat rates of $0 if the admin wishes
134
+ //------------------------------------------------------------------
135
+ $oRate->setCost($fRate);
136
+ if(is_numeric($this->_fFlatRate)) {
137
+ $oRate->setPrice($this->_fFlatRate);
138
+ } else {
139
+ $oRate->setPrice($fRate);
140
+ }
141
+
142
+ //---------------------------
143
+ // Append and return the rate
144
+ //---------------------------
145
+ return $oResult->append($oRate);
146
+ }
147
+
148
+ /**
149
+ * Create the delivery order with Postmates!
150
+ * Use prior quote and selected pickup point stored in the session.
151
+ */
152
+ final public function createDelivery(Mage_Sales_Model_Order $oOrder, $sManifest, $sDropoffNotes='')
153
+ {
154
+ $oShipAddr = $oOrder->getShippingAddress();
155
+ $aShipAddr = self::_addrObjectToArray($oShipAddr);
156
+
157
+ // Build drop off address
158
+ $sDropoffName = $oShipAddr->getFirstname();
159
+ $sDropoffPhone = $oShipAddr->getTelephone();
160
+ $sDropoffCompany = $oShipAddr->getCompany();
161
+ $sDropoffAddr = self::_addressString($aShipAddr);
162
+
163
+ // Fetch the persisted quote and pickup address
164
+ $this->_readConfigData();
165
+ $this->_oMageSession = Mage::getSingleton('core/session');
166
+ $aPostmatesQuote = $this->_retrievePostmatesQuote();
167
+ $aPickupAddr = $this->_retrievePickupAddr();
168
+
169
+ if(!is_array($aPostmatesQuote)) {
170
+ Mage::log('Failed to retrieve the Postmates quote');
171
+ return false;
172
+ }
173
+
174
+ if(!is_array($aPickupAddr)) {
175
+ Mage::log('Failed to retrieve the Pickup address');
176
+ return false;
177
+ }
178
+
179
+ // If the quote has expired we'll have to send the request for delivery without the quote_id
180
+ $oExpires = $aPostmatesQuote['expires'];
181
+ $oNow = new \DateTime('now', $aPostmatesQuote['expires']->getTimezone());
182
+ $iPostmatesQuoteId = $aPostmatesQuote['id'];
183
+ if($oNow > $oExpires) {
184
+ $iPostmatesQuoteId = null;
185
+ }
186
+
187
+ // Place the delivery order with Postmates
188
+ $oClient = $this->_createClient();
189
+ $oDelivery = $oClient->createDelivery(
190
+ $sManifest,
191
+ $aPickupAddr['name'],
192
+ self::_addressString($aPickupAddr),
193
+ $aPickupAddr['phone'],
194
+ $sDropoffName,
195
+ $sDropoffAddr,
196
+ $sDropoffPhone,
197
+ $sDropoffCompany,
198
+ '', // $sManifestReference
199
+ '', // $sPickupBusinessName - TODO maybe populate off the store name or something else ??
200
+ $aPickupAddr['notes'],
201
+ $sDropoffNotes,
202
+ $iPostmatesQuoteId
203
+ );
204
+
205
+ // This is a real edge case, but it's better to provide handling for it than to let orders
206
+ // slip through without having deliveries scheduled with Postmates. The situation is this,
207
+ // we've done our best to determine if the quote is already expired before sending the delivery
208
+ // request. Who knows, maybe there's some clock skew or some other reason, but Postmates could
209
+ // still come back and tell us the first attempt (with a quote_id supplied) was expired. Testing
210
+ // has shown that immediately sending another delivery request with identical data except for
211
+ // the omitting the quote ID results in the same error. Likely we're running into some sort of
212
+ // caching response from the Postmates server. What we'll do then is generate another quote,
213
+ // then use that to create the delivery request. If that doesn't bust the cache, nothing will!
214
+ if($oDelivery->isError() && $oDelivery['code'] == 'expired_quote') {
215
+ // Get a new quote for the already chosen pickup address
216
+ $aError = array();
217
+ $this->_aDropoffAddr = $aShipAddr;
218
+ $oQuote = $this->_getQuote($aPickupAddres, $aError);
219
+
220
+ if($oQuote && !$oQuote->isError()) {
221
+ $oDelivery = $oClient->createDelivery(
222
+ $sManifest,
223
+ $aPickupAddr['name'],
224
+ self::_addressString($aPickupAddr),
225
+ $aPickupAddr['phone'],
226
+ $sDropoffName,
227
+ $sDropoffAddr,
228
+ $sDropoffPhone,
229
+ $sDropoffCompany,
230
+ '', // $sManifestReference
231
+ '', // $sPickupBusinessName - TODO maybe populate off the store name or something else ??
232
+ $aPickupAddr['notes'],
233
+ $sDropoffNotes,
234
+ $oQuote['id']
235
+ );
236
+ }
237
+ }
238
+
239
+ // If the delivery request was a success,
240
+ // clear the stuff we had stored in the session
241
+ if(!$oDelivery->isError()) {
242
+ $this->_cleanupPersistedQuote();
243
+ $this->_cleanupPersistedPickupAddr();
244
+ } else {
245
+ Mage::log('Failed to create Postmates Delivery request: ' . $oDelivery['message'], Zend_Log::ERR);
246
+
247
+ $this->_sendDeliveryNotification(
248
+ $oOrder,
249
+ self::_addressString($aPickupAddr),
250
+ self::_addressString($aShipAddr),
251
+ $oQuote);
252
+ }
253
+
254
+ return $oDelivery;
255
+ }
256
+
257
+ /**
258
+ * Using the Postmates delivery ID from an existing order,
259
+ * fetch the status from Postmates, then pass along the DAO to client code.
260
+ */
261
+ public function getStatus($sDeliveryId)
262
+ {
263
+ $this->_readConfigData();
264
+ $oClient = $this->_createClient();
265
+ return $oClient->getDeliveryStatus($sDeliveryId);
266
+ }
267
+
268
+ //==================================================================================
269
+ // Extensible methods --------------------------------------------------------------
270
+ // ---------------------------------------------------------------------------------
271
+ // The following set of methods are open for you to override in a subclass.
272
+ // Before you override a particular method, make sure to read the comment about
273
+ // what it does, so you understand what you are doing by changing the implementation
274
+ //==================================================================================
275
+
276
+ /**
277
+ * Read data from the configuration.
278
+ * @note Override this method at your own risk!
279
+ * An overridden version must correctly set all the properties this stock implementation does.
280
+ */
281
+ protected function _readConfigData()
282
+ {
283
+ $this->_sApiKey = $this->getConfigData('api_key');
284
+ $this->_sCustomerId = $this->getConfigData('customer_id');
285
+ $this->_bEstimatesEnabled = (bool)$this->getConfigData('estimates_enabled');
286
+ $this->_sCustomTitle = trim($this->getConfigData('title'));
287
+ $this->_sCustomMethod = $this->getConfigData('custom_carrier_method');
288
+ $this->_fFlatRate = $this->getConfigData('flat_rate');
289
+ $this->_bQuoteNotificationsEnabled = (bool)$this->getConfigData('quote_notifications_enabled');
290
+ $this->_sQuoteNotificationEmail = $this->getConfigData('quote_notification_email');
291
+ $this->_iQuoteEmailTemplateId = (int)$this->getConfigData('quote_email_template');
292
+ $this->_bDeliveryNotificationsEnabled = (bool)$this->getConfigData('delivery_notifications_enabled');
293
+ $this->_sDeliveryNotificationEmail = $this->getConfigData('delivery_notification_email');
294
+ $this->_iDeliveryEmailTemplateId = (int)$this->getConfigData('delivery_email_template');
295
+ $this->_aPickupAddrs = unserialize($this->getConfigData('pickup_addresses'));
296
+ }
297
+
298
+ /**
299
+ * Select a subset of pickup addresses to request quotes from Postmates for.
300
+ * The pickup addresses you return should be ideal, based upon the shipping address of the customr,
301
+ * which is stored in $this->_aPickupAddrs.
302
+ *
303
+ * If you override this method, it must return at minimum one entry from $this->_aPickupAddrs.
304
+ * Be careful not to return too many. Each of them will be passed to the Postmates API individually,
305
+ * (there is no bulk quote endpoint), incurring a decent wait for your customer based on the performance
306
+ * of their internet connection. The stock implementation does its best to widdle the pickup addresses
307
+ * down to one, but will return at most three to have quotes generated against.
308
+ */
309
+ protected function _selectPickupAddressesToQuote()
310
+ {
311
+ // Convert the shipping address to a string
312
+ $this->_sDropoffAddr = self::_addressString($this->_aDropoffAddr);
313
+
314
+ // Get the list of configured pickup addresses
315
+ $iNumPickupAddrs = count($this->_aPickupAddrs);
316
+
317
+ // If we have no pickup addresses, just bail...
318
+ if($iNumPickupAddrs < 1) {
319
+ return false;
320
+ }
321
+ // If there's only one pickup address, we can only quote against that
322
+ elseif($iNumPickupAddrs == 1) {
323
+ return $this->_aPickupAddrs;
324
+ }
325
+
326
+ // Last case, there are multiple pickup addresses. In this case we don't want to
327
+ // get quotes on all of them, let's take a look at them and try to come up with a
328
+ // subset that we can get quotes against. We'll end up sticking with the cheapest one.
329
+
330
+ // Start by determining which addresses look the most desirable
331
+ usort($this->_aPickupAddrs, array($this, '_rankPickupAddresses'));
332
+
333
+ // If the winner is vastly higher than the second entry, let's save a lot of time
334
+ // (in network overhead) and only fetch a quote for the best match.
335
+ // Otherwise get quotes against the top 3 addresses.
336
+ $aFirstTwoAddrs = array_slice($this->_aPickupAddrs, 0, 2);
337
+ $iTopScore = $aFirstTwoAddrs[0]['rank'];
338
+ $iSecondScore = $aFirstTwoAddrs[0]['rank'];
339
+ $iNumAddressesToTry = 3;
340
+ if($iTopScore >= $iSecondScore * 2) {
341
+ $iNumAddressesToTry = 1;
342
+ }
343
+
344
+ return array_slice($this->_aPickupAddrs, 0, $iNumAddressesToTry);
345
+ }
346
+
347
+ /**
348
+ * Determine the best quote. By default, this selects the quote with the lowest fee.
349
+ * You may override to use whatever logic you wish instead. You must return only one
350
+ * group of quote/pickup address if you do.
351
+ */
352
+ protected function _determineBestQuote(array $aQuotesAndPickupAddrs)
353
+ {
354
+ $oQuote = null;
355
+ $aPickupAddr = null;
356
+ $fFee = 0;
357
+ $fLowestRate = (float)PHP_INT_MAX;
358
+ foreach($aQuotesAndPickupAddrs as $aQuoteAndPickupAddr) {
359
+ $_oQuote = $aQuoteAndPickupAddr['quote'];
360
+ $fFee = $_oQuote['fee'];
361
+ if($fFee < $fLowestRate) {
362
+ $fLowestRate = $fFee;
363
+ $oQuote = $_oQuote;
364
+ $aPickupAddr = $aQuoteAndPickupAddr['pickup-addr'];
365
+ }
366
+ }
367
+
368
+ return array('quote' => $oQuote, 'pickup-addr' => $aPickupAddr);
369
+ }
370
+
371
+ //==================================================================================
372
+ // Quote / Pickup Address persistence methods --------------------------------------
373
+ // ---------------------------------------------------------------------------------
374
+ // Persiste a Postmates quote and associated pickup address.
375
+ // Currently this module uses the PHP session to persist these values.
376
+ // This is moderately brittle, however in most cases if the session has expired, the
377
+ // customer's cart has been emptied, and in the vast majority of cases, customers
378
+ // will complete a checkout while their session is active. These methods may be
379
+ // overridden, so if you want a stronger persistence mechanism before a future
380
+ // release of the module, you may implement it yourself. Please note
381
+ // WE WILL NOT BE GIVING REFUNDS BECAUSE THE MODULE PERSISTS THESE VALUES IN THE SESSION
382
+ // That has been clearly stated online and you have been given fair warning. That said
383
+ // there are plans to store these values on the Magento quote in a subsequent release
384
+ // in the near future! Notes for the future release follow.
385
+ //
386
+ // Store the Postmates quote on the Magento quote, that way we won't ever lose one
387
+ // Furthermore, we need to check the expiration time of the quote and if it has expired,
388
+ // tee up a fresh quote. The sad thing is the customer will have already checked out, so
389
+ // if we get to that scenario the store owner will run the risk of the delivery costing
390
+ // a bit more than the original quote.
391
+ //==================================================================================
392
+ /**
393
+ * Fetch a persisted Postmates Quote
394
+ * @returns Postmates_Shipping_Client_Dao_DeliveryQuote
395
+ */
396
+ protected function _retrievePostmatesQuote()
397
+ {
398
+ return $this->_oMageSession->getPostmatesQuote();
399
+ }
400
+
401
+ /**
402
+ * Fetch persisted pickup address
403
+ * @returns array
404
+ */
405
+ protected function _retrievePickupAddr()
406
+ {
407
+ return $this->_oMageSession->getPostmatesPickupAddr();
408
+ }
409
+
410
+ /**
411
+ * Store a Postmates quote
412
+ */
413
+ protected function _persistPostmatesQuote(Postmates_Shipping_Client_Dao_DeliveryQuote $oQuote)
414
+ {
415
+ $this->_oMageSession->setPostmatesQuote($oQuote->getArrayCopy());
416
+ }
417
+
418
+ /**
419
+ * Store a pickup address
420
+ */
421
+ protected function _persistPickupAddr(array $aPickupAddress)
422
+ {
423
+ $this->_oMageSession->setPostmatesPickupAddr($aPickupAddress);
424
+ }
425
+
426
+ /**
427
+ * 'Cleanup' (read: remove) a persisted Postmates quote
428
+ * Likely you won't need this if you're using database persistence, but perhaps you'll find it relevant
429
+ */
430
+ protected function _cleanupPersistedQuote()
431
+ {
432
+ $this->_oMageSession->unsPostmatesQuote();
433
+ }
434
+
435
+ /**
436
+ * 'Cleanup' (read: remove) a persisted pickup address
437
+ * Likely you won't need this if you're using database persistence, but perhaps you'll find it relevant
438
+ */
439
+ protected function _cleanupPersistedPickupAddr()
440
+ {
441
+ $this->_oMageSession->unsPostmatesPickupAddr();
442
+ }
443
+
444
+ /**
445
+ * Customize carrier title in the display if the admin would like us to
446
+ *
447
+ * Default Carrier Title is "Postmates"
448
+ * Otherwise, take it from the admin config
449
+ *
450
+ * @note The default value default value in config.xml work as expected
451
+ * because Magento takes an empty value from the admin in system.xml and rolls with it!!
452
+ * We'll protect ourselves in this case and ensure the default is used if the admin has
453
+ * not chosen to customize the carrier title
454
+ */
455
+ protected function _customizeCarrierTitle(Mage_Shipping_Model_Rate_Result_Method $rate)
456
+ {
457
+ if(empty($this->_sCustomTitle)) {
458
+ Mage::app()->getStore()->setConfig('carriers/postmates_shipping/title', 'Postmates');
459
+ }
460
+ }
461
+
462
+ /**
463
+ * Customize shipping method in the display if the admin would like us to
464
+ */
465
+ protected function _customizeCarrierMethod(Mage_Shipping_Model_Rate_Result_Method $rate)
466
+ {
467
+ $method = 'Postmates Delivery';
468
+ if(!empty($this->_sCustomMethod)) {
469
+ $method = $this->_sCustomMethod;
470
+ }
471
+ $rate->setMethod('delivery');
472
+ $rate->setMethodTitle($method);
473
+ }
474
+
475
+ //==================================================================================
476
+ // Closed methods -----------------------------------------------------------------
477
+ // ---------------------------------------------------------------------------------
478
+ // The following set of methods are available for you to call from a subclass,
479
+ // however they are not available for you to override. Doing so would potentially
480
+ // break the template method and main method of this class, collectRates()
481
+ //==================================================================================
482
+
483
+ /**
484
+ * Rank the pickup addresses based on the shipping address of the quote.
485
+ * This is the comparison callback for usort().
486
+ */
487
+ final protected function _rankPickupAddresses(array &$aPickupAddr1, array &$aPickupAddr2)
488
+ {
489
+ $iAddr1Rank = $this->_getPickupAddrRank($this->_aDropoffAddr, $aPickupAddr1);
490
+ $iAddr2Rank = $this->_getPickupAddrRank($this->_aDropoffAddr, $aPickupAddr2);
491
+
492
+ $aPickupAddr1['rank'] = $iAddr1Rank;
493
+ $aPickupAddr2['rank'] = $iAddr2Rank;
494
+
495
+ if($iAddr1Rank == $iAddr2Rank) {
496
+ return 0;
497
+ }
498
+
499
+ return ($iAddr1Rank > $iAddr2Rank) ? -1 : 1;
500
+ }
501
+
502
+ /**
503
+ * Compare a pickup address to a shipipng adddress to determine a rank for the pickup address.
504
+ *
505
+ * @param array $aShipAddr An array representation of the shipping address
506
+ * @param array $aPickupAddr An array representation of the pickup address
507
+ * @returns int
508
+ */
509
+ final protected function _getPickupAddrRank(array $aShipAddr, array $aPickupAddr)
510
+ {
511
+ $sShipCity = strtolower($aShipAddr['city']);
512
+ $sShipState = strtolower($aShipAddr['state']);
513
+ $iShipZip = (int)$aShipAddr['zip'];
514
+
515
+ $sPickupCity = strtolower($aPickupAddr['city']);
516
+ $sPickupState = strtolower($aPickupAddr['state']);
517
+ $iPickupZip = (int)$aPickupAddr['zip'];
518
+
519
+ $iResult = 0;
520
+
521
+ if($sShipState == $sPickupState) {
522
+ if($sShipCity == $sPickupCity) {
523
+ $iResult += 10;
524
+ }
525
+
526
+ if($iShipZip == $iPickupZip) {
527
+ $iResult += 7;
528
+ }
529
+ elseif(abs($iShipZip - $iPickupZip) < 5) {
530
+ $iResult += 6;
531
+ }
532
+ elseif(abs($iShipZip - $iPickupZip) < 10) {
533
+ $iResult += 5;
534
+ }
535
+ elseif(abs($iShipZip - $iPickupZip) < 20) {
536
+ $iResult += 4;
537
+ }
538
+ elseif(abs($iShipZip - $iPickupZip) < 100) {
539
+ $iResult += 3;
540
+ }
541
+ elseif(abs($iShipZip - $iPickupZip) < 200) {
542
+ $iResult += 2;
543
+ }
544
+ elseif(abs($iShipZip - $iPickupZip) < 500) {
545
+ $iResult += 1;
546
+ }
547
+ }
548
+
549
+ return $iResult;
550
+ }
551
+
552
+ /**
553
+ * Request one or more quotes from Postmates using the provided pickup addresses.
554
+ * The pickup addresses to use are determined by the _selectPickupAddressesToQuote()
555
+ * method which you may override if you wish. Once one or more quotes are received,
556
+ * this method selects the least expensive one, and persists it, along with the pickup
557
+ * address that drove the least expensive quote.
558
+ */
559
+ final protected function _fetchQuotes(array $aPickupAddrs)
560
+ {
561
+ // Loop over the provided pickup addresses getting quotes from Postmates for each one
562
+ $aErrors = array();
563
+ $aQuotesAndPickupAddrs = array();
564
+ foreach($aPickupAddrs as $aPickupAddr) {
565
+ $aError = array();
566
+ $oQuote = $this->_getQuote($aPickupAddr, $aError);
567
+
568
+ // Bail if we couldn't get a quote
569
+ if(!$oQuote) {
570
+ $aErrors[] = $aError;
571
+ continue;
572
+ }
573
+
574
+ // Append the quote to our result along with the associated pickup address
575
+ $aQuotesAndPickupAddrs[] = array(
576
+ 'quote' => $oQuote,
577
+ 'pickup-addr' => $aPickupAddr);
578
+ }
579
+
580
+ // Send an email notification if enabled by the admin
581
+ if(count($aQuotesAndPickupAddrs) == 0) {
582
+ $this->_sendQuoteNotification($aErrors, self::_addressString($this->_aDropoffAddr));
583
+
584
+ return false;
585
+ }
586
+
587
+ return $aQuotesAndPickupAddrs;
588
+ }
589
+
590
+ /**
591
+ * Instantiate the Postmates API client via credentials supplied in the admin configuration.
592
+ */
593
+ final protected function _createClient()
594
+ {
595
+ $aCreds = array(
596
+ 'customer_id' => $this->_sCustomerId,
597
+ 'api_key' => $this->_sApiKey
598
+ );
599
+ return new Postmates_Shipping_Client_Client('', $aCreds);
600
+ }
601
+
602
+ /**
603
+ * Get a single quote from Postmates.
604
+ * @note Since the dropoff address will be the same every time, we expect an already validated,
605
+ * string representation to be supplied here.
606
+ */
607
+ final protected function _getQuote(array $aPickupAddress, array &$aError)
608
+ {
609
+ // Bail if the pikcup address is invalid
610
+ if(!self::_addrValid($aPickupAddress)) {
611
+ return false;
612
+ }
613
+
614
+ // Convert the pickup address to a string
615
+ $sPickupAddress = self::_addressString($aPickupAddress);
616
+
617
+ // Request a quote from Postmates
618
+ try {
619
+ $oClient = $this->_createClient();
620
+ $oQuote = $oClient->requestDeliveryQuote($sPickupAddress, $this->_sDropoffAddr);
621
+ if(!$oQuote->isError()) {
622
+ return $oQuote;
623
+ }
624
+
625
+ // Pass error information back to the caller
626
+ $aError['pickup-address'] = $sPickupAddress;
627
+ $aError['code'] = $oQuote['code'];
628
+ $aError['message'] = $oQuote['message'];
629
+
630
+ // We failed to fetch the quote return false
631
+ $sErrMsg =
632
+ 'Error from Postmates API; Code: ' . $oQuote['code'] .
633
+ '; Message: ' . $oQuote['message'];
634
+
635
+ Mage::log($sErrMsg, Zend_Log::ERR);
636
+
637
+ return false;
638
+ }
639
+ // We failed to fetch the quote return false
640
+ catch(Exception $e) {
641
+ $sErrMsg = 'Exception thrown trying to create a Postmates quote - ' . $e->getMessage();
642
+ Mage::log($sErrMsg, Zend_Log::ERR);
643
+
644
+ return false;
645
+ }
646
+ }
647
+
648
+ /**
649
+ * Convert interesting parts of a Magento address object to an array for our purposes.
650
+ */
651
+ static final protected function _addrObjectToArray(Mage_Customer_Model_Address_Abstract $oAddr)
652
+ {
653
+ return array(
654
+ 'address' => $oAddr->getStreetFull(),
655
+ 'city' => $oAddr->getCity(),
656
+ 'state' => $oAddr->getRegionCode(),
657
+ 'zip' => $oAddr->getPostcode()
658
+ );
659
+ }
660
+
661
+ /**
662
+ * Convert a pickup address from the carrier configuration to a string representation.
663
+ */
664
+ static final protected function _addressString(array $aAddress)
665
+ {
666
+ $sAddr = '';
667
+ if(!empty($aAddress['address'])) {
668
+ $sAddr = $aAddress['address'] . ', ';
669
+ }
670
+
671
+ return
672
+ $sAddr .
673
+ $aAddress['city'] . ', ' .
674
+ $aAddress['state'] . ' ' .
675
+ $aAddress['zip'];
676
+ }
677
+
678
+ /**
679
+ * Determine if an address is valid. Our only criteria here is that the state and the zip are non-empty.
680
+ * If $bMinimalAddrOk == false (the default), then the street address and the city must also be non-empty.
681
+ * $bMinimalAddrOk is provided to support fetching quotes when only a state and zip are provided by the
682
+ * estimation widget on the cart page.
683
+ */
684
+ static final protected function _addrValid(array $addr, $bMinimalAddrOk=false)
685
+ {
686
+ if(!$bMinimalAddrOk && (empty($addr['address']) || empty($addr['city']))) {
687
+ return false;
688
+ }
689
+
690
+ return
691
+ !empty($addr['state']) &&
692
+ !empty($addr['zip']);
693
+ }
694
+
695
+ /**
696
+ * Send an email to the configured address if we've failed to create a Postmates quote.
697
+ * This behavior is configurable.
698
+ */
699
+ protected function _sendQuoteNotification(array $aErrors, $sShipAddr)
700
+ {
701
+ // Bail if quote notifications are disabled
702
+ if(!$this->_bQuoteNotificationsEnabled) {
703
+ return;
704
+ }
705
+
706
+ // Coalesce error information into an HTML format
707
+ $sErrors = '';
708
+ foreach($aErrors as $aError) {
709
+ $sErrors .=
710
+ '<p><ul>' .
711
+ '<li><strong>Pickup Address</strong> <em>' . $aError['pickup-address'] . '</em></li>' .
712
+ '<li><strong>Postmates Error Message</strong> <em>' . $aError['message'] . '</em></li>' .
713
+ '<li><strong>Postmates Error Code</strong> <em>' . $aError['code'] . '</em></li>' .
714
+ '</ul></p>';
715
+ }
716
+
717
+ // If this was for an estimate, strip off the missing street component
718
+ if($sShipAddr[0] == ',') {
719
+ $sShipAddr = substr($sShipAddr, 2);
720
+ }
721
+
722
+ $aTemplateParams = array(
723
+ 'quoteErrors' => $sErrors,
724
+ 'shippingAddress' => $sShipAddr
725
+ );
726
+
727
+ $this->_sendNotificationEmail(
728
+ $this->_iQuoteEmailTemplateId, $this->_sQuoteNotificationEmail, $aTemplateParams);
729
+ }
730
+
731
+ /**
732
+ * Send an email to the configured address if we've failed to create a Postmates delivery request.
733
+ * This behavior is configurable.
734
+ */
735
+ protected function _sendDeliveryNotification(
736
+ $oOrder, $sPickupAddr, $sShipAddr, Postmates_Shipping_Client_BaseDao $oError
737
+ ) {
738
+ // Bail if error notifications are disabled
739
+ if(!$this->_bDeliveryNotificationsEnabled) {
740
+ return;
741
+ }
742
+
743
+ $aTemplateParams = array(
744
+ 'pickupAddress' => $sPickupAddr,
745
+ 'shippingAddress' => $sShipAddr,
746
+ 'errorCode' => $oError['code'],
747
+ 'errorMessage' => $oError['message'],
748
+ 'order' => $oOrder
749
+ );
750
+
751
+ $this->_sendNotificationEmail(
752
+ $this->_iDeliveryEmailTemplateId, $this->_sDeliveryNotificationEmail, $aTemplateParams);
753
+ }
754
+
755
+ /**
756
+ * Send a notification email. This is largely configurable and overridable.
757
+ */
758
+ protected function _sendNotificationEmail($iTemplateId, $sEmailHandle, array $aTemplateParams)
759
+ {
760
+ try {
761
+ $oMailer = Mage::getModel('core/email_template_mailer');
762
+
763
+ // Target is configurable
764
+ $oEmailInfo = Mage::getModel('core/email_info');
765
+ $oEmailInfo->addTo(
766
+ $this->_getStoreEmailAddressSenderOption($sEmailHandle, 'email'),
767
+ $this->_getStoreEmailAddressSenderOption($sEmailHandle, 'name'));
768
+ $oMailer->addEmailInfo($oEmailInfo);
769
+
770
+ // Sender always uses the general handle
771
+ $oMailer->setSender(array(
772
+ 'name' => $this->_getStoreEmailAddressSenderOption('general', 'name'),
773
+ 'email' => $this->_getStoreEmailAddressSenderOption('general', 'email'),
774
+ ));
775
+
776
+ // TODO Support multiple stores
777
+ // The extension is known not to support multiple websites / stores yet.
778
+ // REFUNDS WILL NOT BE ISSUED FOR THIS REASON
779
+ // $oMailer->setStoreId($storeId);
780
+ $oMailer->setTemplateId($iTemplateId);
781
+ $oMailer->setTemplateParams($aTemplateParams);
782
+
783
+ $oMailer->send();
784
+ } catch (Exception $e) {
785
+ Mage::logException($e);
786
+ }
787
+ }
788
+
789
+ /**
790
+ * @note This method taken from open source extension
791
+ * https://github.com/ajzele/Inchoo_AdminOrderNotifier
792
+ * @param $identType ('general' or 'sales' or 'support' or 'custom1' or 'custom2')
793
+ * @param $option ('name' or 'email')
794
+ * @return string
795
+ */
796
+ private function _getStoreEmailAddressSenderOption($identType, $option)
797
+ {
798
+ if (!$generalContactName = Mage::getSingleton('core/config_data')->getCollection()->getItemByColumnValue('path', 'trans_email/ident_'.$identType.'/'.$option)) {
799
+ $conf = Mage::getSingleton('core/config')->init()->getXpath('/config/default/trans_email/ident_'.$identType.'/'.$option);
800
+ $generalContactName = array_shift($conf);
801
+ } else {
802
+ $generalContactName = $generalContactName->getValue();
803
+ }
804
+
805
+ return (string)$generalContactName;
806
+ }
807
+ }
app/code/community/Postmates/Shipping/Model/Emails.php~ ADDED
File without changes
app/code/community/Postmates/Shipping/Model/Observer.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+ /**
14
+ * Schedule a delivery request with Postmates for a recently completed order.
15
+ */
16
+ class Postmates_Shipping_Model_Observer
17
+ {
18
+ /**
19
+ * Once an order is successfully placed, this method will create a delivery
20
+ * for orders that have chosen the postmates_shipping shipping method.
21
+ */
22
+ final public function createDelivery(Varien_Event_Observer $oObserver)
23
+ {
24
+ $oOrder = $oObserver->getData('order');
25
+
26
+ // Determine if the shipping method is postmates_shipping_delivery
27
+ $sShipMethod = $oOrder->getShippingMethod();
28
+ if($sShipMethod != 'postmates_shipping_delivery') {
29
+ Mage::log('Failed to find the Postmates quote in the session', Zend_Log::ERR);
30
+ return;
31
+ }
32
+
33
+ // Create the delivery request with Postmates
34
+ $oCarrier = Mage::getModel('postmates_shipping/carrier');
35
+ $oDelivery = $oCarrier->createDelivery(
36
+ $oOrder,
37
+ $this->_buildManifest($oOrder),
38
+ $this->_getDropoffNotes($oOrder));
39
+
40
+ // Bail if we failed to create a delivery request to Postmates.
41
+ // Error handling for this scenario resides in the Carrier class.
42
+ if(!is_object($oDelivery) || $oDelivery->isError()) {
43
+ return $this;
44
+ }
45
+
46
+ //------------------------------------------
47
+ // Track the postmates delivery on the order
48
+ //------------------------------------------
49
+ $oOrder->setPostmatesDeliveryId($oDelivery['id']);
50
+ $oOrder->save();
51
+
52
+ return $this;
53
+ }
54
+
55
+ /**
56
+ * TODO Ability to capture dropoff notes from customer during checkout.
57
+ * Feel free to implement this in a subclass for now if you wish.
58
+ */
59
+ protected function _getDropoffNotes(Mage_Sales_Model_Order $oOrder)
60
+ {
61
+
62
+ }
63
+
64
+ /**
65
+ * Build the manifest for the Postmates Delivery request.
66
+ * All we're doing here is concatenating the names of each item from the order by newlines.
67
+ */
68
+ protected function _buildManifest(Mage_Sales_Model_Order $oOrder)
69
+ {
70
+ $sManifest = '';
71
+ $aManifest = array();
72
+ foreach($oOrder->getAllItems() as $oItem) {
73
+ $aManifest[] = $oItem->getName();
74
+ }
75
+
76
+ if(count($aManifest) == 1) {
77
+ $sManifest = $aManifest[0];
78
+ } else {
79
+ $sManifest = implode(",\n", $aManifest);
80
+ }
81
+
82
+ if(empty($sManifest)) {
83
+ Mage::log($sManifest, Zend_Log::ERR);
84
+ }
85
+
86
+ return $sManifest;
87
+ }
88
+ }
app/code/community/Postmates/Shipping/Model/Observer.php~ ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Postmates_Shipping_Model_Observer
3
+ {
4
+ /**
5
+ * Once an order is successfully placed, this method will create a delivery
6
+ * for orders that have chosen the postmates_shipping shipping method.
7
+ */
8
+ public function createDelivery(Varien_Event_Observer $oObserver)
9
+ {
10
+ $oOrder = $oObserver->getData('order');
11
+
12
+ // TODO Determine if the shipping method is postmates_shipping
13
+
14
+ // TODO Build the manifest
15
+
16
+ // Build drop off address
17
+ $oShipAddress = $oOrder->getShippingAddress();
18
+ $sDropoffName = $oShipAddress->getFirstname();
19
+ $sDropoffPhone = $oShipAddress->getTelephone();
20
+ $sDropoffCompany = $oShipAddress->getCompany();
21
+ $sDropoffAddr =
22
+ $oShipAddress->getStreetFull() . PHP_EOL .
23
+ $oShipAddress->getCity() . ', ' .
24
+ $oShipAddress->getState() . ' ' .
25
+ $oShipAddress->getPostcode();
26
+
27
+ // TODO Populate these from the carrier configuration
28
+ $sPickupNotes = '';
29
+
30
+ // TODO Ability to capture dropoff notes from customer during checkout
31
+ $sDropoffNotes = '';
32
+
33
+ // Create the delivery request with Postmates
34
+ // TODO What if this fails ???
35
+ $carrier = new Postmates_Shipping_Model_Carrier();
36
+ $carrier->createDelivery(
37
+ $sManifest,
38
+ $sDropoffName,
39
+ $sDropoffPhone,
40
+ $sDropoffAddr,
41
+ $sDropoffCompany,
42
+ $sDropoffNotes);
43
+
44
+ // Clear the shopping cart by marking the quote inactive
45
+ // XXX Verify this works...
46
+ $oQuote = $oObserver->getData('quote')->setIsActive(0)->save();
47
+ }
48
+ }
app/code/community/Postmates/Shipping/design/sales/order/info.phtml ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Academic Free License (AFL 3.0)
8
+ * that is bundled with this package in the file LICENSE_AFL.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/afl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magento.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magento.com for more information.
20
+ *
21
+ * @category design
22
+ * @package base_default
23
+ * @copyright Copyright (c) 2006-2015 X.commerce, Inc. (http://www.magento.com)
24
+ * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
25
+ */
26
+ ?>
27
+ <?php /** @var $this Mage_Sales_Block_Order_Info */ ?>
28
+ <?php $_order = $this->getOrder() ?>
29
+ <?php echo $this->getMessagesBlock()->toHtml() ?>
30
+ <div class="page-title title-buttons">
31
+ <h1><?php echo $this->__('Order #%s - %s', $_order->getRealOrderId(), $_order->getStatusLabel()) ?></h1>
32
+ <?php echo $this->getChildHtml('buttons') ?>
33
+ </div>
34
+ <?php echo $this->getStatusHistoryRssUrl($_order) ?>
35
+ <dl class="order-info">
36
+ <dt><?php echo $this->__('About This Order:') ?></dt>
37
+ <dd>
38
+ <?php $_links = $this->getLinks(); ?>
39
+ <ul id="order-info-tabs">
40
+ <?php foreach ($_links as $_link): ?>
41
+ <?php if($_link->getUrl()): ?>
42
+ <li><a href="<?php echo $_link->getUrl() ?>"><?php echo $_link->getLabel() ?></a></li>
43
+ <?php else: ?>
44
+ <li class="current"><?php echo $_link->getLabel() ?></li>
45
+ <?php endif; ?>
46
+ <?php endforeach; ?>
47
+ </ul>
48
+ <script type="text/javascript">decorateGeneric($('order-info-tabs').select('LI'),['first','last']);</script>
49
+ </dd>
50
+ </dl>
51
+ <p class="order-date"><?php echo $this->__('Order Date: %s', $this->formatDate($_order->getCreatedAtStoreDate(), 'long')) ?></p>
52
+ <?php if (!$_order->getIsVirtual()): ?>
53
+ <div class="col2-set order-info-box">
54
+ <div class="col-1">
55
+ <div class="box">
56
+ <div class="box-title">
57
+ <h2><?php echo $this->__('Shipping Address') ?></h2>
58
+ </div>
59
+ <div class="box-content">
60
+ <address><?php echo $_order->getShippingAddress()->format('html') ?></address>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ <div class="col-2">
65
+
66
+ <div class="box">
67
+ <div class="box-title">
68
+ <h2><?php echo $this->__('Shipping Method') ?></h2>
69
+ </div>
70
+ <div class="box-content">
71
+ <?php if ($_order->getShippingDescription()): ?>
72
+ <?php echo $this->escapeHtml($_order->getShippingDescription()) ?>
73
+ <?php else: ?>
74
+ <p><?php echo $this->helper('sales')->__('No shipping information available'); ?></p>
75
+ <?php endif; ?>
76
+ </div>
77
+ </div>
78
+
79
+ </div>
80
+ <div class="col-2">
81
+
82
+ <div class="box">
83
+ <div class="box-title">
84
+ <h2>Status</h2>
85
+ </div>
86
+ <div class="box-content">
87
+ <?php echo $this->getDeliveryStatus(); ?>
88
+ </div>
89
+ </div>
90
+
91
+ </div>
92
+ </div>
93
+ <?php endif; ?>
94
+ <div class="col2-set order-info-box">
95
+ <div class="col-1">
96
+ <div class="box">
97
+ <div class="box-title">
98
+ <h2><?php echo $this->__('Billing Address') ?></h2>
99
+ </div>
100
+ <div class="box-content">
101
+ <address><?php echo $_order->getBillingAddress()->format('html') ?></address>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ <div class="col-2">
106
+ <div class="box box-payment">
107
+ <div class="box-title">
108
+ <h2><?php echo $this->__('Payment Method') ?></h2>
109
+ </div>
110
+ <div class="box-content">
111
+ <?php echo $this->getPaymentInfoHtml() ?>
112
+ </div>
113
+ </div>
114
+ </div>
115
+ </div>
app/code/community/Postmates/Shipping/design/sales/order/info.phtml~ ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Academic Free License (AFL 3.0)
8
+ * that is bundled with this package in the file LICENSE_AFL.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/afl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magento.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magento.com for more information.
20
+ *
21
+ * @category design
22
+ * @package base_default
23
+ * @copyright Copyright (c) 2006-2015 X.commerce, Inc. (http://www.magento.com)
24
+ * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
25
+ */
26
+ ?>
27
+ <?php /** @var $this Mage_Sales_Block_Order_Info */ ?>
28
+ <?php $_order = $this->getOrder() ?>
29
+ <?php echo $this->getMessagesBlock()->toHtml() ?>
30
+ <div class="page-title title-buttons">
31
+ <h1><?php echo $this->__('Order #%s - %s', $_order->getRealOrderId(), $_order->getStatusLabel()) ?></h1>
32
+ <?php echo $this->getChildHtml('buttons') ?>
33
+ </div>
34
+ <?php echo $this->getStatusHistoryRssUrl($_order) ?>
35
+ <dl class="order-info">
36
+ <dt><?php echo $this->__('About This Order:') ?></dt>
37
+ <dd>
38
+ <?php $_links = $this->getLinks(); ?>
39
+ <ul id="order-info-tabs">
40
+ <?php foreach ($_links as $_link): ?>
41
+ <?php if($_link->getUrl()): ?>
42
+ <li><a href="<?php echo $_link->getUrl() ?>"><?php echo $_link->getLabel() ?></a></li>
43
+ <?php else: ?>
44
+ <li class="current"><?php echo $_link->getLabel() ?></li>
45
+ <?php endif; ?>
46
+ <?php endforeach; ?>
47
+ </ul>
48
+ <script type="text/javascript">decorateGeneric($('order-info-tabs').select('LI'),['first','last']);</script>
49
+ </dd>
50
+ </dl>
51
+ <p class="order-date"><?php echo $this->__('Order Date: %s', $this->formatDate($_order->getCreatedAtStoreDate(), 'long')) ?></p>
52
+ <?php if (!$_order->getIsVirtual()): ?>
53
+ <div class="col2-set order-info-box">
54
+ <div class="col-1">
55
+ <div class="box">
56
+ <div class="box-title">
57
+ <h2><?php echo $this->__('Shipping Address') ?></h2>
58
+ </div>
59
+ <div class="box-content">
60
+ <address><?php echo $_order->getShippingAddress()->format('html') ?></address>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ <div class="col-2">
65
+ <div class="box">
66
+ <div class="box-title">
67
+ <h2><?php echo $this->__('Shipping Method') ?></h2>
68
+ <h3><?php die(var_dump(get_class($_order))); ?></h3>
69
+ <h3><?php die(var_dump($_order->getPostmatesDeliveryId()))?></h3>
70
+ </div>
71
+ <div class="box-content">
72
+ <?php if ($_order->getShippingDescription()): ?>
73
+ <?php echo $this->escapeHtml($_order->getShippingDescription()) ?>
74
+ <?php else: ?>
75
+ <p><?php echo $this->helper('sales')->__('No shipping information available'); ?></p>
76
+ <?php endif; ?>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ <?php endif; ?>
82
+ <div class="col2-set order-info-box">
83
+ <div class="col-1">
84
+ <div class="box">
85
+ <div class="box-title">
86
+ <h2><?php echo $this->__('Billing Address') ?></h2>
87
+ </div>
88
+ <div class="box-content">
89
+ <address><?php echo $_order->getBillingAddress()->format('html') ?></address>
90
+ </div>
91
+ </div>
92
+ </div>
93
+ <div class="col-2">
94
+ <div class="box box-payment">
95
+ <div class="box-title">
96
+ <h2><?php echo $this->__('Payment Method') ?></h2>
97
+ </div>
98
+ <div class="box-content">
99
+ <?php echo $this->getPaymentInfoHtml() ?>
100
+ </div>
101
+ </div>
102
+ </div>
103
+ </div>
app/code/community/Postmates/Shipping/docs/Postmates-Shipping-Documentation.pdf ADDED
Binary file
app/code/community/Postmates/Shipping/etc/config.xml ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <!--
4
+ /**
5
+ * NOTICE OF COPYRIGHT
6
+ *
7
+ * This file is copywritten material only to be used if it has been purchased.
8
+ * See the Magento Connect website for information to pay if you have not already
9
+ * and any warranty or support information.
10
+ *
11
+ * @category Postmates
12
+ * @package Postmates_Shipping
13
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
14
+ */
15
+ -->
16
+ <modules>
17
+ <Postmates_Shipping>
18
+ <version>1.3.0</version>
19
+ </Postmates_Shipping>
20
+ </modules>
21
+
22
+ <global>
23
+ <models>
24
+ <postmates_shipping>
25
+ <class>Postmates_Shipping_Model</class>
26
+ </postmates_shipping>
27
+ </models>
28
+
29
+ <blocks>
30
+ <postmates_shipping>
31
+ <class>Postmates_Shipping_Block</class>
32
+ </postmates_shipping>
33
+ <sales>
34
+ <rewrite>
35
+ <order_info>Postmates_Shipping_Block_Sales_Order_Info</order_info>
36
+ </rewrite>
37
+ </sales>
38
+ </blocks>
39
+
40
+ <template>
41
+ <email>
42
+ <sales_postmates_shipping_delivery_email_template>
43
+ <label>Postmates Shipping Delivery Error</label>
44
+ <file>postmates_shipping_delivery_error.html</file>
45
+ <type>html</type>
46
+ </sales_postmates_shipping_delivery_email_template>
47
+
48
+ <sales_postmates_shipping_quote_email_template>
49
+ <label>Postmates Shipping Quote Error</label>
50
+ <file>postmates_shipping_quote_error.html</file>
51
+ <type>html</type>
52
+ </sales_postmates_shipping_quote_email_template>
53
+ </email>
54
+ </template>
55
+
56
+ <resources>
57
+ <postmates_shipping_setup>
58
+ <setup>
59
+ <module>Postmates_Shipping</module>
60
+ <model>mage_sales/resource_setup</model>
61
+ </setup>
62
+ </postmates_shipping_setup>
63
+ </resources>
64
+
65
+ </global>
66
+
67
+ <default>
68
+ <!-- custom carrier to get quotes from postmates -->
69
+ <carriers>
70
+ <postmates_shipping>
71
+ <active>1</active>
72
+ <model>postmates_shipping/carrier</model>
73
+ <title>Postmates</title>
74
+ <custom_carrier_method>Postmates Delivery</custom_carrier_method>
75
+ <sort_order>20</sort_order>
76
+ <sallowspecific>0</sallowspecific>
77
+ </postmates_shipping>
78
+ </carriers>
79
+ </default>
80
+
81
+ <frontend>
82
+ <events>
83
+ <!-- observer to create the delivery once the order is completed -->
84
+ <checkout_submit_all_after>
85
+ <observers>
86
+ <Postmates_Shipping_Customevent>
87
+ <type>singleton</type>
88
+ <model>postmates_shipping/observer</model>
89
+ <method>createDelivery</method>
90
+ </Postmates_Shipping_Customevent>
91
+ </observers>
92
+ </checkout_submit_all_after>
93
+ </events>
94
+ </frontend>
95
+
96
+ </config>
app/code/community/Postmates/Shipping/etc/system.xml ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /**
4
+ * NOTICE OF COPYRIGHT
5
+ *
6
+ * This file is copywritten material only to be used if it has been purchased.
7
+ * See the Magento Connect website for information to pay if you have not already
8
+ * and any warranty or support information.
9
+ *
10
+ * @category Postmates
11
+ * @package Postmates_Shipping
12
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
13
+ */
14
+ -->
15
+ <config>
16
+ <sections>
17
+ <carriers>
18
+ <groups>
19
+ <postmates_shipping translate="label">
20
+ <label>Postmates Shipping</label>
21
+ <sort_order>2</sort_order>
22
+ <show_in_default>1</show_in_default>
23
+ <show_in_website>0</show_in_website>
24
+ <show_in_store>0</show_in_store>
25
+ <fields>
26
+ <active translate="label">
27
+ <label>Enabled</label>
28
+ <frontend_type>select</frontend_type>
29
+ <source_model>adminhtml/system_config_source_yesno</source_model>
30
+ <sort_order>1</sort_order>
31
+ <show_in_default>1</show_in_default>
32
+ <show_in_website>0</show_in_website>
33
+ <show_in_store>0</show_in_store>
34
+ </active>
35
+
36
+ <!-- Postmates configuration -->
37
+ <customer_id translate="label">
38
+ <label>Customer ID</label>
39
+ <frontend_type>text</frontend_type>
40
+ <sort_order>2</sort_order>
41
+ <show_in_default>1</show_in_default>
42
+ <show_in_website>0</show_in_website>
43
+ <show_in_store>0</show_in_store>
44
+ </customer_id>
45
+ <api_key translate="label">
46
+ <label>API Key</label>
47
+ <frontend_type>text</frontend_type>
48
+ <sort_order>3</sort_order>
49
+ <show_in_default>1</show_in_default>
50
+ <show_in_website>0</show_in_website>
51
+ <show_in_store>0</show_in_store>
52
+ </api_key>
53
+
54
+ <!-- Appearance customization -->
55
+ <title translate="label">
56
+ <label>Custom Carrier Title</label>
57
+ <comment>This is what the shipping carrier will be titled in the frontend. The default is "Postmates"</comment>
58
+ <frontend_type>text</frontend_type>
59
+ <sort_order>4</sort_order>
60
+ <show_in_default>1</show_in_default>
61
+ <show_in_website>0</show_in_website>
62
+ <show_in_store>0</show_in_store>
63
+ </title>
64
+ <custom_carrier_method translate="label">
65
+ <label>Custom Carrier Method</label>
66
+ <comment>This is what the shipping method will be methodd in the frontend. The default is "Postmates Delivery"</comment>
67
+ <frontend_type>text</frontend_type>
68
+ <sort_order>5</sort_order>
69
+ <show_in_default>1</show_in_default>
70
+ <show_in_website>0</show_in_website>
71
+ <show_in_store>0</show_in_store>
72
+ </custom_carrier_method>
73
+
74
+ <!-- Behavior customization -->
75
+ <flat_rate translate="label">
76
+ <label>Flat rate charge</label>
77
+ <comment>If set, this is the charge to the customer instead of the charge of their quote</comment>
78
+ <frontend_type>text</frontend_type>
79
+ <sort_order>6</sort_order>
80
+ <show_in_default>1</show_in_default>
81
+ <show_in_website>0</show_in_website>
82
+ <show_in_store>0</show_in_store>
83
+ </flat_rate>
84
+ <estimates_enabled translate="label">
85
+ <label>Estimates Enabled</label>
86
+ <comment>If set, customers will be able to get estimated shipping charges, however, they may not be perfectly accurate since a full street address is not available in the estimation widget. Estimates will always work if you have a flat rate enabled.</comment>
87
+ <frontend_type>select</frontend_type>
88
+ <source_model>adminhtml/system_config_source_yesno</source_model>
89
+ <sort_order>7</sort_order>
90
+ <show_in_default>1</show_in_default>
91
+ <show_in_website>0</show_in_website>
92
+ <show_in_store>0</show_in_store>
93
+ </estimates_enabled>
94
+
95
+ <!-- Quote notifications -->
96
+ <quote_notifications_enabled translate="label">
97
+ <label>Send quote error notifications</label>
98
+ <comment>If the extension fails to create a Postmates quote request for a given delivery address, send a notification email.</comment>
99
+ <frontend_type>select</frontend_type>
100
+ <source_model>adminhtml/system_config_source_yesno</source_model>
101
+ <sort_order>8</sort_order>
102
+ <show_in_default>1</show_in_default>
103
+ <show_in_website>0</show_in_website>
104
+ <show_in_store>0</show_in_store>
105
+ </quote_notifications_enabled>
106
+ <quote_notification_email translate="label comment">
107
+ <label>Quote Notification Email</label>
108
+ <comment>Select an email to be notified if a Postmates quote request is not created for a given delivery address.</comment>
109
+ <frontend_type>select</frontend_type>
110
+ <source_model>postmates_shipping/adminhtml_emails</source_model>
111
+ <sort_order>9</sort_order>
112
+ <show_in_default>1</show_in_default>
113
+ <show_in_website>0</show_in_website>
114
+ <show_in_store>0</show_in_store>
115
+ </quote_notification_email>
116
+ <quote_email_template translate="label">
117
+ <label>Quote Email Template</label>
118
+ <frontend_type>select</frontend_type>
119
+ <source_model>adminhtml/system_config_source_email_template</source_model>
120
+ <sort_order>10</sort_order>
121
+ <show_in_default>1</show_in_default>
122
+ <show_in_website>0</show_in_website>
123
+ <show_in_store>0</show_in_store>
124
+ </quote_email_template>
125
+
126
+ <!-- Delivery notifications -->
127
+ <delivery_notifications_enabled translate="label">
128
+ <label>Send delivery error notifications</label>
129
+ <comment>If the extension fails to create a Postmates delivery request for an order, send a notification email. This is highly recommended.</comment>
130
+ <frontend_type>select</frontend_type>
131
+ <source_model>adminhtml/system_config_source_yesno</source_model>
132
+ <sort_order>11</sort_order>
133
+ <show_in_default>1</show_in_default>
134
+ <show_in_website>0</show_in_website>
135
+ <show_in_store>0</show_in_store>
136
+ </delivery_notifications_enabled>
137
+ <delivery_notification_email translate="label comment">
138
+ <label>Delivery Notification Email</label>
139
+ <comment>Select an email to be notified if a Postmates delivery request is not created for a given order.</comment>
140
+ <frontend_type>select</frontend_type>
141
+ <source_model>postmates_shipping/adminhtml_emails</source_model>
142
+ <sort_order>12</sort_order>
143
+ <show_in_default>1</show_in_default>
144
+ <show_in_website>0</show_in_website>
145
+ <show_in_store>0</show_in_store>
146
+ </delivery_notification_email>
147
+ <delivery_email_template translate="label">
148
+ <label>Delivery Email Template</label>
149
+ <frontend_type>select</frontend_type>
150
+ <source_model>adminhtml/system_config_source_email_template</source_model>
151
+ <sort_order>13</sort_order>
152
+ <show_in_default>1</show_in_default>
153
+ <show_in_website>0</show_in_website>
154
+ <show_in_store>0</show_in_store>
155
+ </delivery_email_template>
156
+
157
+ <!-- Pickup addresses -->
158
+ <pickup_addresses>
159
+ <label>Pickup Addresses</label>
160
+ <frontend_type>select</frontend_type>
161
+ <frontend_model>postmates_shipping/adminhtml_pickupaddress</frontend_model>
162
+ <backend_model>postmates_shipping/adminhtml_config_serialized</backend_model>
163
+ <sort_order>14</sort_order>
164
+ <show_in_default>1</show_in_default>
165
+ <show_in_website>0</show_in_website>
166
+ <show_in_store>0</show_in_store>
167
+ </pickup_addresses>
168
+ </fields>
169
+ </postmates_shipping>
170
+ </groups>
171
+ </carriers>
172
+ </sections>
173
+ </config>
app/code/community/Postmates/Shipping/sql/postmates_shipping_setup/install-1.0.0.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF COPYRIGHT
4
+ *
5
+ * This file is copywritten material only to be used if it has been purchased.
6
+ * See the Magento Connect website for information to pay if you have not already
7
+ * and any warranty or support information.
8
+ *
9
+ * @category Postmates
10
+ * @package Postmates_Shipping
11
+ * @copyright Copyright (c) 2015 Moxune LLC (http://moxune.com)
12
+ */
13
+
14
+ /**
15
+ * Add a field to the order model to track Postmates delivery IDs.
16
+ */
17
+ $installer = $this;
18
+
19
+ $installer->startSetup();
20
+ $installer->addAttribute("order", "postmates_delivery_id", array("type"=>"varchar"));
21
+ $installer->endSetup();
package.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Postmates</name>
4
+ <version>1.3.0</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://opensource.org/licenses/mit-license.php">MIT</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Activate on-demand, one hour delivery in your city with the Postmates Magento Extension. </summary>
10
+ <description>The Fastest Way to Deliver Anything in Your City. Integrate a world-class local delivery platform into your Magento store using the Postmates Shipping module. Get started today. </description>
11
+ <notes>Only works with 1.9.x series of Magento.</notes>
12
+ <authors><author><name>Andrew Mager</name><user>postmates</user><email>mager@postmates.com</email></author></authors>
13
+ <date>2016-03-04</date>
14
+ <time>14:57:09</time>
15
+ <contents><target name="magecommunity"><dir name="Postmates"><dir name="Shipping"><dir name="Block"><dir name="Adminhtml"><file name="Pickupaddress.php" hash="501d51fbad2cfb0252a8c3fa6df2eda1"/><file name="Pickupaddress.php~" hash="a0bc028f5e246657914370fd03e53c26"/></dir><dir name="Order"><file name="Info.php~" hash="d41d8cd98f00b204e9800998ecf8427e"/></dir><dir name="Sales"><dir name="Order"><file name="Info.php" hash="41830a6974c9f07881a96bd28e39a0dc"/><file name="Info.php~" hash="9c01bcd876fcaffd5c3a0c9b37ce4ab4"/></dir></dir></dir><dir name="Client"><file name="BaseDao.php" hash="1bef128fe893c3303cab34d774a9a5bc"/><file name="Client.php" hash="e0deb8d472959a50b3ffee014c42ccc2"/><dir name="Dao"><file name="Delivery.php" hash="632d70341e4dd28b79a7f673212eeaca"/><file name="DeliveryQuote.php" hash="762046591d169cebf1d0ca2185e2a2c0"/><file name="Error.php" hash="a971b06a6ae39bbfd68d3ea801298952"/><file name="ListResponse.php" hash="024f05acf786f1bf9661eb6c7d35b65e"/><file name="PList.php" hash="163047104493e96086b516da6b8b6bb4"/><file name="Response.php" hash="00b078c2c7f4b5d9bb948e1a980e73e7"/><file name="User.php" hash="3be15ce08c04c6b70c636ce61f7863a9"/></dir><file name="Factory.php" hash="4bf08bda06baca4ef73179da57fcdebc"/></dir><dir name="Model"><dir name="Adminhtml"><dir name="Config"><file name="Serialized.php" hash="6fbb1752598480d62a82a906e8d681dc"/><file name="Serialized.php~" hash="d29390398c390c785e1dfa6426b7a102"/></dir><file name="Emails.php" hash="81ccd6c4501ed1e094f05a6f80569881"/><file name="Emails.php~" hash="45e40b7f5e16bfa5a5a8b44c65d57d4a"/></dir><file name="Carrier.php" hash="c02dc26eb9b4c57452cad2a26951f9bb"/><file name="Emails.php~" hash="d41d8cd98f00b204e9800998ecf8427e"/><file name="Observer.php" hash="1a993f4f7fdb7d0e11e352b68d92302d"/><file name="Observer.php~" hash="7c1ef18267fa173fb022edcb79cdcd09"/></dir><dir name="design"><dir name="sales"><dir name="order"><file name="info.phtml" hash="35500aecaca8fb12ddccfc7c5640f05b"/><file name="info.phtml~" hash="0b90fdc9f613740d6de8c0f3ea2d903a"/></dir></dir></dir><dir name="docs"><file name="Postmates-Shipping-Documentation.pdf" hash="6a8f0d85570084267ee1c668bb3c8f5e"/></dir><dir name="etc"><file name="config.xml" hash="36e5010f40102dbef5f7c5644f0feae0"/><file name="system.xml" hash="c1403abca008518d92f61e4583591553"/></dir><dir name="sql"><dir name="postmates_shipping_setup"><file name="install-1.0.0.php" hash="b4ba2332d28252e195afb1c3d749e2ad"/></dir></dir></dir></dir></target></contents>
16
+ <compatible/>
17
+ <dependencies><required><php><min>5.3.0</min><max>6.0.0</max></php></required></dependencies>
18
+ </package>