Version Notes
- Full refactor of the module
- Add two grids to the backend to see the abandoned carts
- Add a log database table to easily see what's going on
- Implement an autologin link system
- Implement Google Campaign tags
- Improve the templates to list all items
- Change the way dryrun and test email behaves
- Add notification flags columns to the native abandoned carts report
Download this release
Release Info
| Developer | Digital Pianism |
| Extension | DigitalPianism_Abandonedcarts |
| Version | 1.0.0 |
| Comparing to | |
| See all releases | |
Code changes from version 0.3.6 to 1.0.0
- app/code/community/DigitalPianism/Abandonedcarts/Block/Adminhtml/Abandonedcarts.php +49 -0
- app/code/community/DigitalPianism/Abandonedcarts/Block/Adminhtml/Abandonedcarts/Grid.php +210 -0
- app/code/community/DigitalPianism/Abandonedcarts/Block/Adminhtml/Logs.php +20 -0
- app/code/community/DigitalPianism/Abandonedcarts/Block/Adminhtml/Logs/Grid.php +76 -0
- app/code/community/DigitalPianism/Abandonedcarts/Block/Adminhtml/Saleabandonedcarts.php +49 -0
- app/code/community/DigitalPianism/Abandonedcarts/Block/Adminhtml/Saleabandonedcarts/Grid.php +218 -0
- app/code/community/DigitalPianism/Abandonedcarts/Block/Adminhtml/System/Config/Form/Button.php +0 -54
- app/code/community/DigitalPianism/Abandonedcarts/Helper/Data.php +58 -26
- app/code/community/DigitalPianism/Abandonedcarts/Model/Adminhtml/Observer.php +87 -0
- app/code/community/DigitalPianism/Abandonedcarts/Model/Collection.php +280 -0
- app/code/community/DigitalPianism/Abandonedcarts/Model/Link.php +14 -0
- app/code/community/DigitalPianism/Abandonedcarts/Model/Link/Cleaner.php +31 -0
- app/code/community/DigitalPianism/Abandonedcarts/Model/Log.php +25 -0
- app/code/community/DigitalPianism/Abandonedcarts/Model/Notifier.php +637 -0
- app/code/community/DigitalPianism/Abandonedcarts/Model/Observer.php +0 -742
- app/code/community/DigitalPianism/Abandonedcarts/Model/Resource/Link.php +14 -0
- app/code/community/DigitalPianism/Abandonedcarts/Model/Resource/Link/Collection.php +14 -0
- app/code/community/DigitalPianism/Abandonedcarts/Model/Resource/Log.php +14 -0
- app/code/community/DigitalPianism/Abandonedcarts/Model/Resource/Log/Collection.php +14 -0
- app/code/community/DigitalPianism/Abandonedcarts/controllers/Adminhtml/AbandonedcartsController.php +113 -11
- app/code/community/DigitalPianism/Abandonedcarts/controllers/IndexController.php +50 -0
- app/code/community/DigitalPianism/Abandonedcarts/data/abandonedcarts_setup/data-upgrade-1.0.0-1.0.1.php +33 -0
- app/code/community/DigitalPianism/Abandonedcarts/etc/adminhtml.xml +60 -0
- app/code/community/DigitalPianism/Abandonedcarts/etc/config.xml +94 -5
- app/code/community/DigitalPianism/Abandonedcarts/etc/system.xml +106 -43
- app/code/community/DigitalPianism/Abandonedcarts/sql/abandonedcarts_setup/upgrade-0.3.6-1.0.0.php +27 -0
- app/design/adminhtml/default/default/layout/digitalpianism/abandonedcarts.xml +10 -0
- app/design/adminhtml/default/default/template/digitalpianism/abandonedcarts/list.phtml +12 -0
- app/design/adminhtml/default/default/template/digitalpianism/abandonedcarts/system/config/button.phtml +0 -17
- app/design/frontend/base/default/template/digitalpianism/abandonedcarts/email/items.phtml +26 -0
- app/design/frontend/base/default/template/digitalpianism/abandonedcarts/email/sale_items.phtml +40 -0
- app/locale/en_US/DigitalPianism_Abandonedcarts.csv +32 -4
- app/locale/en_US/template/email/digitalpianism/abandonedcarts/sales_abandonedcarts.html +3 -5
- app/locale/en_US/template/email/digitalpianism/abandonedcarts/sales_abandonedcarts_sale.html +4 -10
- app/locale/fr_FR/DigitalPianism_Abandonedcarts.csv +33 -5
- app/locale/fr_FR/template/email/digitalpianism/abandonedcarts/sales_abandonedcarts.html +4 -6
- app/locale/fr_FR/template/email/digitalpianism/abandonedcarts/sales_abandonedcarts_sale.html +5 -12
- package.xml +12 -5
app/code/community/DigitalPianism/Abandonedcarts/Block/Adminhtml/Abandonedcarts.php
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Class DigitalPianism_Abandonedcarts_Block_Adminhtml_Abandonedcarts
|
| 5 |
+
*/
|
| 6 |
+
class DigitalPianism_Abandonedcarts_Block_Adminhtml_Abandonedcarts extends Mage_Adminhtml_Block_Widget_Grid_Container
|
| 7 |
+
{
|
| 8 |
+
/**
|
| 9 |
+
* Constructor
|
| 10 |
+
*/
|
| 11 |
+
public function __construct()
|
| 12 |
+
{
|
| 13 |
+
$this->_controller = 'adminhtml_abandonedcarts';
|
| 14 |
+
$this->_blockGroup = 'abandonedcarts';
|
| 15 |
+
$this->_headerText = Mage::helper('abandonedcarts')->__('Abandoned Carts (Applied delay: %s days)', Mage::getStoreConfig('abandonedcartsconfig/options/notify_delay'));
|
| 16 |
+
parent::__construct();
|
| 17 |
+
$this->_removeButton('add');
|
| 18 |
+
$this->_addButton('notify', array(
|
| 19 |
+
'label' => Mage::helper('abandonedcarts')->__('Send notifications'),
|
| 20 |
+
'onclick' => "setLocation('".$this->getUrl('*/*/notifyAll')."')",
|
| 21 |
+
));
|
| 22 |
+
$this->setTemplate('digitalpianism/abandonedcarts/list.phtml');
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
/**
|
| 26 |
+
* Prepare the layout
|
| 27 |
+
*/
|
| 28 |
+
protected function _prepareLayout()
|
| 29 |
+
{
|
| 30 |
+
// Display store switcher if system has more one store
|
| 31 |
+
if (!Mage::app()->isSingleStoreMode())
|
| 32 |
+
{
|
| 33 |
+
$this->setChild('store_switcher', $this->getLayout()->createBlock('adminhtml/store_switcher')
|
| 34 |
+
->setUseConfirm(false)
|
| 35 |
+
->setSwitchUrl($this->getUrl('*/*/*', array('store' => null)))
|
| 36 |
+
);
|
| 37 |
+
}
|
| 38 |
+
return parent::_prepareLayout();
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
/**
|
| 42 |
+
* Getter for the store switcher HTML
|
| 43 |
+
*/
|
| 44 |
+
public function getStoreSwitcherHtml()
|
| 45 |
+
{
|
| 46 |
+
return $this->getChildHtml('store_switcher');
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
}
|
app/code/community/DigitalPianism/Abandonedcarts/Block/Adminhtml/Abandonedcarts/Grid.php
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Class DigitalPianism_Abandonedcarts_Block_Adminhtml_Abandonedcarts_Grid
|
| 5 |
+
*/
|
| 6 |
+
class DigitalPianism_Abandonedcarts_Block_Adminhtml_Abandonedcarts_Grid extends Mage_Adminhtml_Block_Widget_Grid
|
| 7 |
+
{
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
*
|
| 11 |
+
*/
|
| 12 |
+
public function __construct()
|
| 13 |
+
{
|
| 14 |
+
parent::__construct();
|
| 15 |
+
$this->setId('abandonedcartsGrid');
|
| 16 |
+
$this->setDefaultSort('cart_updated_at');
|
| 17 |
+
$this->setDefaultDir('DESC');
|
| 18 |
+
$this->setSaveParametersInSession(true);
|
| 19 |
+
$this->setUseAjax(true);
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
/**
|
| 23 |
+
* @return mixed
|
| 24 |
+
*/
|
| 25 |
+
protected function _getStore()
|
| 26 |
+
{
|
| 27 |
+
$storeId = (int) $this->getRequest()->getParam('store', 0);
|
| 28 |
+
return Mage::app()->getStore($storeId);
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
protected function _prepareCollection()
|
| 32 |
+
{
|
| 33 |
+
// Delay
|
| 34 |
+
$delay = Mage::getStoreConfig('abandonedcartsconfig/options/notify_delay');
|
| 35 |
+
$delay = date('Y-m-d H:i:s', time() - $delay * 24 * 3600);
|
| 36 |
+
|
| 37 |
+
// Default store and website
|
| 38 |
+
$defaults = $this->_getDefaultStoreAndWebsite();
|
| 39 |
+
|
| 40 |
+
// Store and website from the multistore switcher
|
| 41 |
+
$store = $this->_getStore();
|
| 42 |
+
if ($storeId = $store->getId())
|
| 43 |
+
{
|
| 44 |
+
$defaults = array(
|
| 45 |
+
$storeId,
|
| 46 |
+
Mage::getModel('core/store')->load($storeId)->getWebsiteId()
|
| 47 |
+
);
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
$collection = Mage::getModel('abandonedcarts/collection')->getCollection($delay, $defaults[0], $defaults[1]);
|
| 51 |
+
|
| 52 |
+
// Group by to have a nice grid
|
| 53 |
+
if (Mage::helper('catalog/product_flat')->isEnabled()) {
|
| 54 |
+
$collection->getSelect()->columns(
|
| 55 |
+
array(
|
| 56 |
+
'product_ids' => 'GROUP_CONCAT(e.entity_id)',
|
| 57 |
+
'product_names' => 'GROUP_CONCAT(catalog_flat.name)',
|
| 58 |
+
'product_prices' => 'SUM(catalog_flat.price)'
|
| 59 |
+
)
|
| 60 |
+
);
|
| 61 |
+
} else {
|
| 62 |
+
$collection->getSelect()->columns(
|
| 63 |
+
array(
|
| 64 |
+
'product_ids' => 'GROUP_CONCAT(e.entity_id)',
|
| 65 |
+
'product_names' => 'GROUP_CONCAT(catalog_name.value)',
|
| 66 |
+
'product_prices' => 'SUM(catalog_price.value)'
|
| 67 |
+
)
|
| 68 |
+
);
|
| 69 |
+
}
|
| 70 |
+
$collection->getSelect()->group('customer_email');
|
| 71 |
+
|
| 72 |
+
$this->setCollection($collection);
|
| 73 |
+
return parent::_prepareCollection();
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
protected function _prepareColumns()
|
| 77 |
+
{
|
| 78 |
+
$this->addColumn('customer_email', array(
|
| 79 |
+
'header' => Mage::helper('abandonedcarts')->__('Customer Email'),
|
| 80 |
+
'index' => 'customer_email',
|
| 81 |
+
'filter_condition_callback' => array($this, 'filterCallback')
|
| 82 |
+
));
|
| 83 |
+
|
| 84 |
+
$this->addColumn('customer_firstname', array(
|
| 85 |
+
'header' => Mage::helper('abandonedcarts')->__('Customer Firstname'),
|
| 86 |
+
'index' => 'customer_firstname',
|
| 87 |
+
'filter_condition_callback' => array($this, 'filterCallback')
|
| 88 |
+
));
|
| 89 |
+
|
| 90 |
+
$this->addColumn('customer_lastname', array(
|
| 91 |
+
'header' => Mage::helper('abandonedcarts')->__('Customer Lastname'),
|
| 92 |
+
'index' => 'customer_lastname',
|
| 93 |
+
'filter_condition_callback' => array($this, 'filterCallback')
|
| 94 |
+
));
|
| 95 |
+
|
| 96 |
+
$this->addColumn('product_ids', array(
|
| 97 |
+
'header' => Mage::helper('abandonedcarts')->__('Product Ids'),
|
| 98 |
+
'index' => 'product_ids',
|
| 99 |
+
'filter_index' => "e.entity_id",
|
| 100 |
+
'filter_condition_callback' => array($this, 'filterEqualCallback')
|
| 101 |
+
));
|
| 102 |
+
|
| 103 |
+
$this->addColumn('product_names', array(
|
| 104 |
+
'header' => Mage::helper('abandonedcarts')->__('Product Names'),
|
| 105 |
+
'index' => 'product_names',
|
| 106 |
+
'filter_index' => (Mage::helper('catalog/product_flat')->isEnabled() ? "catalog_flat.name" : "catalog_name.value"),
|
| 107 |
+
'filter_condition_callback' => array($this, 'filterEqualCallback')
|
| 108 |
+
));
|
| 109 |
+
|
| 110 |
+
$this->addColumn('product_prices', array(
|
| 111 |
+
'header' => Mage::helper('abandonedcarts')->__('Cart Total'),
|
| 112 |
+
'index' => 'product_prices',
|
| 113 |
+
'filter' => false
|
| 114 |
+
));
|
| 115 |
+
|
| 116 |
+
// Output format for the start and end dates
|
| 117 |
+
$outputFormat = Mage::app()->getLocale()->getDateTimeFormat(Mage_Core_Model_Locale::FORMAT_TYPE_MEDIUM);
|
| 118 |
+
|
| 119 |
+
$this->addColumn('cart_updated_at', array(
|
| 120 |
+
'header' => Mage::helper('abandonedcarts')->__('Cart Updated At'),
|
| 121 |
+
'index' => 'cart_updated_at',
|
| 122 |
+
'type' => 'datetime',
|
| 123 |
+
'format' => $outputFormat,
|
| 124 |
+
'default' => ' -- ',
|
| 125 |
+
'filter_index' => 'quote_table.updated_at',
|
| 126 |
+
'filter_condition_callback' => array($this, 'filterDateCallback')
|
| 127 |
+
));
|
| 128 |
+
|
| 129 |
+
return parent::_prepareColumns();
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
/**
|
| 133 |
+
* @return $this
|
| 134 |
+
*/
|
| 135 |
+
protected function _prepareMassaction()
|
| 136 |
+
{
|
| 137 |
+
$this->setMassactionIdField('customer_email');
|
| 138 |
+
$this->getMassactionBlock()->setFormFieldName('abandonedcarts');
|
| 139 |
+
|
| 140 |
+
$this->getMassactionBlock()->addItem('notify', array(
|
| 141 |
+
'label' => Mage::helper('abandonedcarts')->__('Send notification'),
|
| 142 |
+
'url' => $this->getUrl('*/*/notify')
|
| 143 |
+
));
|
| 144 |
+
|
| 145 |
+
return $this;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
/**
|
| 149 |
+
* @return array
|
| 150 |
+
*/
|
| 151 |
+
protected function _getDefaultStoreAndWebsite()
|
| 152 |
+
{
|
| 153 |
+
foreach (Mage::app()->getWebsites() as $website) {
|
| 154 |
+
// Get the website id
|
| 155 |
+
$websiteId = $website->getWebsiteId();
|
| 156 |
+
foreach ($website->getGroups() as $group) {
|
| 157 |
+
$stores = $group->getStores();
|
| 158 |
+
foreach ($stores as $store) {
|
| 159 |
+
|
| 160 |
+
// Get the store id
|
| 161 |
+
$storeId = $store->getStoreId();
|
| 162 |
+
break 3;
|
| 163 |
+
}
|
| 164 |
+
}
|
| 165 |
+
}
|
| 166 |
+
return array($storeId, $websiteId);
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
/**
|
| 170 |
+
* @return string
|
| 171 |
+
*/
|
| 172 |
+
public function getGridUrl()
|
| 173 |
+
{
|
| 174 |
+
return $this->getUrl('*/*/grid', array('current' => true));
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
/**
|
| 178 |
+
* @param $collection
|
| 179 |
+
* @param $column
|
| 180 |
+
*/
|
| 181 |
+
public function filterCallback($collection, $column)
|
| 182 |
+
{
|
| 183 |
+
$field = $column->getFilterIndex() ? $column->getFilterIndex() : $column->getIndex();
|
| 184 |
+
$value = $column->getFilter()->getValue();
|
| 185 |
+
$collection->getSelect()->where("$field like ?", '%' . $value . '%');
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
/**
|
| 189 |
+
* @param $collection
|
| 190 |
+
* @param $column
|
| 191 |
+
*/
|
| 192 |
+
public function filterEqualCallback($collection, $column)
|
| 193 |
+
{
|
| 194 |
+
$field = $column->getFilterIndex() ? $column->getFilterIndex() : $column->getIndex();
|
| 195 |
+
$value = $column->getFilter()->getValue();
|
| 196 |
+
$collection->getSelect()->where("$field = ?", $value);
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
/**
|
| 200 |
+
* @param $collection
|
| 201 |
+
* @param $column
|
| 202 |
+
*/
|
| 203 |
+
public function filterDateCallback($collection, $column)
|
| 204 |
+
{
|
| 205 |
+
$field = $column->getFilterIndex() ? $column->getFilterIndex() : $column->getIndex();
|
| 206 |
+
$value = $column->getFilter()->getValue();
|
| 207 |
+
$collection->getSelect()->where("$field > '" . $value['from']->toString('Y-MM-dd HH:mm:ss') . "' AND $field < '" . $value['to']->toString('Y-MM-dd HH:mm:ss') . "'");
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
}
|
app/code/community/DigitalPianism/Abandonedcarts/Block/Adminhtml/Logs.php
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Class DigitalPianism_Abandonedcarts_Block_Adminhtml_Logs
|
| 5 |
+
*/
|
| 6 |
+
class DigitalPianism_Abandonedcarts_Block_Adminhtml_Logs extends Mage_Adminhtml_Block_Widget_Grid_Container
|
| 7 |
+
{
|
| 8 |
+
/**
|
| 9 |
+
* Constructor
|
| 10 |
+
*/
|
| 11 |
+
public function __construct()
|
| 12 |
+
{
|
| 13 |
+
$this->_controller = 'adminhtml_logs';
|
| 14 |
+
$this->_blockGroup = 'abandonedcarts';
|
| 15 |
+
$this->_headerText = Mage::helper('abandonedcarts')->__('Abandoned Carts Logs');
|
| 16 |
+
parent::__construct();
|
| 17 |
+
$this->_removeButton('add');
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
}
|
app/code/community/DigitalPianism/Abandonedcarts/Block/Adminhtml/Logs/Grid.php
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Class DigitalPianism_Abandonedcarts_Block_Adminhtml_Logs_Grid
|
| 5 |
+
*/
|
| 6 |
+
class DigitalPianism_Abandonedcarts_Block_Adminhtml_Logs_Grid extends Mage_Adminhtml_Block_Widget_Grid
|
| 7 |
+
{
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
*
|
| 11 |
+
*/
|
| 12 |
+
public function __construct()
|
| 13 |
+
{
|
| 14 |
+
parent::__construct();
|
| 15 |
+
$this->setId('abandonedcartLogsGrid');
|
| 16 |
+
$this->setDefaultSort('log_id');
|
| 17 |
+
$this->setDefaultDir('DESC');
|
| 18 |
+
$this->setSaveParametersInSession(true);
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
protected function _prepareCollection()
|
| 22 |
+
{
|
| 23 |
+
$collection = Mage::getResourceModel('abandonedcarts/log_collection');
|
| 24 |
+
$this->setCollection($collection);
|
| 25 |
+
return parent::_prepareCollection();
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
protected function _prepareColumns()
|
| 29 |
+
{
|
| 30 |
+
$this->addColumn('customer_email', array(
|
| 31 |
+
'header' => Mage::helper('abandonedcarts')->__('Customer Email'),
|
| 32 |
+
'index' => 'customer_email',
|
| 33 |
+
));
|
| 34 |
+
|
| 35 |
+
$this->addColumn('type', array(
|
| 36 |
+
'header' => Mage::helper('abandonedcarts')->__('Type'),
|
| 37 |
+
'index' => 'type',
|
| 38 |
+
'type' => 'options',
|
| 39 |
+
'options' => Mage::getModel('abandonedcarts/log')->toOptionArray()
|
| 40 |
+
));
|
| 41 |
+
|
| 42 |
+
$this->addColumn('comment', array(
|
| 43 |
+
'header' => Mage::helper('abandonedcarts')->__('Comment'),
|
| 44 |
+
'index' => 'comment',
|
| 45 |
+
));
|
| 46 |
+
|
| 47 |
+
$this->addColumn('store', array(
|
| 48 |
+
'header' => Mage::helper('abandonedcarts')->__('Store #'),
|
| 49 |
+
'index' => 'store',
|
| 50 |
+
));
|
| 51 |
+
|
| 52 |
+
$this->addColumn('dryrun', array(
|
| 53 |
+
'header' => Mage::helper('abandonedcarts')->__('Dry Run'),
|
| 54 |
+
'type' => 'options',
|
| 55 |
+
'index' => 'dryrun',
|
| 56 |
+
'options' => array(
|
| 57 |
+
0 => Mage::helper('abandonedcarts')->__("No"),
|
| 58 |
+
1 => Mage::helper('abandonedcarts')->__("Yes")
|
| 59 |
+
)
|
| 60 |
+
));
|
| 61 |
+
|
| 62 |
+
// Output format for the start and end dates
|
| 63 |
+
$outputFormat = Mage::app()->getLocale()->getDateTimeFormat(Mage_Core_Model_Locale::FORMAT_TYPE_MEDIUM);
|
| 64 |
+
|
| 65 |
+
$this->addColumn('added', array(
|
| 66 |
+
'header' => Mage::helper('abandonedcarts')->__('Date'),
|
| 67 |
+
'index' => 'added',
|
| 68 |
+
'type' => 'datetime',
|
| 69 |
+
'format' => $outputFormat,
|
| 70 |
+
'default' => ' -- '
|
| 71 |
+
));
|
| 72 |
+
|
| 73 |
+
return parent::_prepareColumns();
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
}
|
app/code/community/DigitalPianism/Abandonedcarts/Block/Adminhtml/Saleabandonedcarts.php
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Class DigitalPianism_Abandonedcarts_Block_Adminhtml_Saleabandonedcarts
|
| 5 |
+
*/
|
| 6 |
+
class DigitalPianism_Abandonedcarts_Block_Adminhtml_Saleabandonedcarts extends Mage_Adminhtml_Block_Widget_Grid_Container
|
| 7 |
+
{
|
| 8 |
+
/**
|
| 9 |
+
* Constructor
|
| 10 |
+
*/
|
| 11 |
+
public function __construct()
|
| 12 |
+
{
|
| 13 |
+
$this->_controller = 'adminhtml_saleabandonedcarts';
|
| 14 |
+
$this->_blockGroup = 'abandonedcarts';
|
| 15 |
+
$this->_headerText = Mage::helper('abandonedcarts')->__('Sale Abandoned Carts');
|
| 16 |
+
parent::__construct();
|
| 17 |
+
$this->_removeButton('add');
|
| 18 |
+
$this->_addButton('notify', array(
|
| 19 |
+
'label' => Mage::helper('abandonedcarts')->__('Send notifications'),
|
| 20 |
+
'onclick' => "setLocation('".$this->getUrl('*/*/notifySaleAll')."')",
|
| 21 |
+
));
|
| 22 |
+
$this->setTemplate('digitalpianism/abandonedcarts/list.phtml');
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
/**
|
| 26 |
+
* Prepare the layout
|
| 27 |
+
*/
|
| 28 |
+
protected function _prepareLayout()
|
| 29 |
+
{
|
| 30 |
+
// Display store switcher if system has more one store
|
| 31 |
+
if (!Mage::app()->isSingleStoreMode())
|
| 32 |
+
{
|
| 33 |
+
$this->setChild('store_switcher', $this->getLayout()->createBlock('adminhtml/store_switcher')
|
| 34 |
+
->setUseConfirm(false)
|
| 35 |
+
->setSwitchUrl($this->getUrl('*/*/*', array('store' => null)))
|
| 36 |
+
);
|
| 37 |
+
}
|
| 38 |
+
return parent::_prepareLayout();
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
/**
|
| 42 |
+
* Getter for the store switcher HTML
|
| 43 |
+
*/
|
| 44 |
+
public function getStoreSwitcherHtml()
|
| 45 |
+
{
|
| 46 |
+
return $this->getChildHtml('store_switcher');
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
}
|
app/code/community/DigitalPianism/Abandonedcarts/Block/Adminhtml/Saleabandonedcarts/Grid.php
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Class DigitalPianism_Abandonedcarts_Block_Adminhtml_Saleabandonedcarts_Grid
|
| 5 |
+
*/
|
| 6 |
+
class DigitalPianism_Abandonedcarts_Block_Adminhtml_Saleabandonedcarts_Grid extends Mage_Adminhtml_Block_Widget_Grid
|
| 7 |
+
{
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
*
|
| 11 |
+
*/
|
| 12 |
+
public function __construct()
|
| 13 |
+
{
|
| 14 |
+
parent::__construct();
|
| 15 |
+
$this->setId('saleabandonedcartsGrid');
|
| 16 |
+
$this->setDefaultSort('cart_updated_at');
|
| 17 |
+
$this->setDefaultDir('DESC');
|
| 18 |
+
$this->setSaveParametersInSession(true);
|
| 19 |
+
$this->setUseAjax(true);
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
/**
|
| 23 |
+
* @return mixed
|
| 24 |
+
*/
|
| 25 |
+
protected function _getStore()
|
| 26 |
+
{
|
| 27 |
+
$storeId = (int) $this->getRequest()->getParam('store', 0);
|
| 28 |
+
return Mage::app()->getStore($storeId);
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
protected function _prepareCollection()
|
| 32 |
+
{
|
| 33 |
+
// Default store and website
|
| 34 |
+
$defaults = $this->_getDefaultStoreAndWebsite();
|
| 35 |
+
|
| 36 |
+
// Store and website from the multistore switcher
|
| 37 |
+
$store = $this->_getStore();
|
| 38 |
+
if ($storeId = $store->getId())
|
| 39 |
+
{
|
| 40 |
+
$defaults = array(
|
| 41 |
+
$storeId,
|
| 42 |
+
Mage::getModel('core/store')->load($storeId)->getWebsiteId()
|
| 43 |
+
);
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
$collection = Mage::getModel('abandonedcarts/collection')->getSalesCollection($defaults[0], $defaults[1]);
|
| 47 |
+
|
| 48 |
+
// Group by to have a nice grid
|
| 49 |
+
$collection->getSelect()->group('customer_email');
|
| 50 |
+
|
| 51 |
+
if (Mage::helper('catalog/product_flat')->isEnabled()) {
|
| 52 |
+
$collection->getSelect()->columns(
|
| 53 |
+
array(
|
| 54 |
+
'product_ids' => 'GROUP_CONCAT(e.entity_id)',
|
| 55 |
+
'product_names' => 'GROUP_CONCAT(catalog_flat.name)',
|
| 56 |
+
'product_prices' => 'SUM(quote_items.price)',
|
| 57 |
+
'product_special_prices' => 'SUM(IFNULL(catalog_flat.special_price,quote_items.price))',
|
| 58 |
+
)
|
| 59 |
+
);
|
| 60 |
+
|
| 61 |
+
$collection->getSelect()->having("SUM(quote_items.price) < SUM(IFNULL(catalog_flat.special_price,quote_items.price))");
|
| 62 |
+
} else {
|
| 63 |
+
$collection->getSelect()->columns(
|
| 64 |
+
array(
|
| 65 |
+
'product_ids' => 'GROUP_CONCAT(e.entity_id)',
|
| 66 |
+
'product_names' => 'GROUP_CONCAT(catalog_name.value)',
|
| 67 |
+
'product_prices' => 'SUM(quote_items.price)',
|
| 68 |
+
'product_special_prices' => 'SUM(IFNULL(catalog_sprice.value,quote_items.price))',
|
| 69 |
+
)
|
| 70 |
+
);
|
| 71 |
+
|
| 72 |
+
$collection->getSelect()->having("SUM(quote_items.price) > SUM(IFNULL(catalog_sprice.value,quote_items.price))");
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
$this->setCollection($collection);
|
| 76 |
+
return parent::_prepareCollection();
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
protected function _prepareColumns()
|
| 80 |
+
{
|
| 81 |
+
$this->addColumn('customer_email', array(
|
| 82 |
+
'header' => Mage::helper('abandonedcarts')->__('Customer Email'),
|
| 83 |
+
'index' => 'customer_email',
|
| 84 |
+
'filter_condition_callback' => array($this, 'filterCallback')
|
| 85 |
+
));
|
| 86 |
+
|
| 87 |
+
$this->addColumn('customer_firstname', array(
|
| 88 |
+
'header' => Mage::helper('abandonedcarts')->__('Customer Firstname'),
|
| 89 |
+
'index' => 'customer_firstname',
|
| 90 |
+
'filter_condition_callback' => array($this, 'filterCallback')
|
| 91 |
+
));
|
| 92 |
+
|
| 93 |
+
$this->addColumn('customer_lastname', array(
|
| 94 |
+
'header' => Mage::helper('abandonedcarts')->__('Customer Lastname'),
|
| 95 |
+
'index' => 'customer_lastname',
|
| 96 |
+
'filter_condition_callback' => array($this, 'filterCallback')
|
| 97 |
+
));
|
| 98 |
+
|
| 99 |
+
$this->addColumn('product_ids', array(
|
| 100 |
+
'header' => Mage::helper('abandonedcarts')->__('Product Ids'),
|
| 101 |
+
'index' => 'product_ids',
|
| 102 |
+
'filter_index' => "e.entity_id",
|
| 103 |
+
'filter_condition_callback' => array($this, 'filterEqualCallback')
|
| 104 |
+
));
|
| 105 |
+
|
| 106 |
+
$this->addColumn('product_names', array(
|
| 107 |
+
'header' => Mage::helper('abandonedcarts')->__('Product Names'),
|
| 108 |
+
'index' => 'product_names',
|
| 109 |
+
'filter_index' => (Mage::helper('catalog/product_flat')->isEnabled() ? "catalog_flat.name" : "catalog_name.value"),
|
| 110 |
+
'filter_condition_callback' => array($this, 'filterEqualCallback')
|
| 111 |
+
));
|
| 112 |
+
|
| 113 |
+
$this->addColumn('product_prices', array(
|
| 114 |
+
'header' => Mage::helper('abandonedcarts')->__('Cart Regular Total'),
|
| 115 |
+
'index' => 'product_prices',
|
| 116 |
+
'filter' => false
|
| 117 |
+
));
|
| 118 |
+
|
| 119 |
+
$this->addColumn('product_special_prices', array(
|
| 120 |
+
'header' => Mage::helper('abandonedcarts')->__('Cart Sale Total'),
|
| 121 |
+
'index' => 'product_special_prices',
|
| 122 |
+
'filter' => false
|
| 123 |
+
));
|
| 124 |
+
|
| 125 |
+
// Output format for the start and end dates
|
| 126 |
+
$outputFormat = Mage::app()->getLocale()->getDateTimeFormat(Mage_Core_Model_Locale::FORMAT_TYPE_MEDIUM);
|
| 127 |
+
|
| 128 |
+
$this->addColumn('cart_updated_at', array(
|
| 129 |
+
'header' => Mage::helper('abandonedcarts')->__('Cart Updated At'),
|
| 130 |
+
'index' => 'cart_updated_at',
|
| 131 |
+
'type' => 'datetime',
|
| 132 |
+
'format' => $outputFormat,
|
| 133 |
+
'default' => ' -- ',
|
| 134 |
+
'filter_index' => 'quote_table.updated_at',
|
| 135 |
+
'filter_condition_callback' => array($this, 'filterDateCallback')
|
| 136 |
+
));
|
| 137 |
+
|
| 138 |
+
return parent::_prepareColumns();
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
/**
|
| 142 |
+
* @return $this
|
| 143 |
+
*/
|
| 144 |
+
protected function _prepareMassaction()
|
| 145 |
+
{
|
| 146 |
+
$this->setMassactionIdField('customer_email');
|
| 147 |
+
$this->getMassactionBlock()->setFormFieldName('abandonedcarts');
|
| 148 |
+
|
| 149 |
+
$this->getMassactionBlock()->addItem('notifySale', array(
|
| 150 |
+
'label' => Mage::helper('abandonedcarts')->__('Send notification'),
|
| 151 |
+
'url' => $this->getUrl('*/*/notifySale')
|
| 152 |
+
));
|
| 153 |
+
|
| 154 |
+
return $this;
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
/**
|
| 158 |
+
* @return array
|
| 159 |
+
*/
|
| 160 |
+
protected function _getDefaultStoreAndWebsite()
|
| 161 |
+
{
|
| 162 |
+
foreach (Mage::app()->getWebsites() as $website) {
|
| 163 |
+
// Get the website id
|
| 164 |
+
$websiteId = $website->getWebsiteId();
|
| 165 |
+
foreach ($website->getGroups() as $group) {
|
| 166 |
+
$stores = $group->getStores();
|
| 167 |
+
foreach ($stores as $store) {
|
| 168 |
+
|
| 169 |
+
// Get the store id
|
| 170 |
+
$storeId = $store->getStoreId();
|
| 171 |
+
break 3;
|
| 172 |
+
}
|
| 173 |
+
}
|
| 174 |
+
}
|
| 175 |
+
return array($storeId, $websiteId);
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
/**
|
| 179 |
+
* @return string
|
| 180 |
+
*/
|
| 181 |
+
public function getGridUrl()
|
| 182 |
+
{
|
| 183 |
+
return $this->getUrl('*/*/salegrid', array('current' => true));
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
/**
|
| 187 |
+
* @param $collection
|
| 188 |
+
* @param $column
|
| 189 |
+
*/
|
| 190 |
+
public function filterCallback($collection, $column)
|
| 191 |
+
{
|
| 192 |
+
$field = $column->getFilterIndex() ? $column->getFilterIndex() : $column->getIndex();
|
| 193 |
+
$value = $column->getFilter()->getValue();
|
| 194 |
+
$collection->getSelect()->where("$field like ?", '%' . $value . '%');
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
/**
|
| 198 |
+
* @param $collection
|
| 199 |
+
* @param $column
|
| 200 |
+
*/
|
| 201 |
+
public function filterEqualCallback($collection, $column)
|
| 202 |
+
{
|
| 203 |
+
$field = $column->getFilterIndex() ? $column->getFilterIndex() : $column->getIndex();
|
| 204 |
+
$value = $column->getFilter()->getValue();
|
| 205 |
+
$collection->getSelect()->where("$field = ?", $value);
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
/**
|
| 209 |
+
* @param $collection
|
| 210 |
+
* @param $column
|
| 211 |
+
*/
|
| 212 |
+
public function filterDateCallback($collection, $column)
|
| 213 |
+
{
|
| 214 |
+
$field = $column->getFilterIndex() ? $column->getFilterIndex() : $column->getIndex();
|
| 215 |
+
$value = $column->getFilter()->getValue();
|
| 216 |
+
$collection->getSelect()->where("$field > '" . $value['from']->toString('Y-MM-dd HH:mm:ss') . "' AND $field < '" . $value['to']->toString('Y-MM-dd HH:mm:ss') . "'");
|
| 217 |
+
}
|
| 218 |
+
}
|
app/code/community/DigitalPianism/Abandonedcarts/Block/Adminhtml/System/Config/Form/Button.php
DELETED
|
@@ -1,54 +0,0 @@
|
|
| 1 |
-
<?php
|
| 2 |
-
|
| 3 |
-
/**
|
| 4 |
-
* Class DigitalPianism_Abandonedcarts_Block_Adminhtml_System_Config_Form_Button
|
| 5 |
-
*/
|
| 6 |
-
class DigitalPianism_Abandonedcarts_Block_Adminhtml_System_Config_Form_Button extends Mage_Adminhtml_Block_System_Config_Form_Field
|
| 7 |
-
{
|
| 8 |
-
/*
|
| 9 |
-
* Set template
|
| 10 |
-
*/
|
| 11 |
-
protected function _construct()
|
| 12 |
-
{
|
| 13 |
-
parent::_construct();
|
| 14 |
-
$this->setTemplate('digitalpianism/abandonedcarts/system/config/button.phtml');
|
| 15 |
-
}
|
| 16 |
-
|
| 17 |
-
/**
|
| 18 |
-
* Return element html
|
| 19 |
-
*
|
| 20 |
-
* @param Varien_Data_Form_Element_Abstract $element
|
| 21 |
-
* @return string
|
| 22 |
-
*/
|
| 23 |
-
protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
|
| 24 |
-
{
|
| 25 |
-
return $this->_toHtml();
|
| 26 |
-
}
|
| 27 |
-
|
| 28 |
-
/**
|
| 29 |
-
* Return ajax url for button
|
| 30 |
-
*
|
| 31 |
-
* @return string
|
| 32 |
-
*/
|
| 33 |
-
public function getAjaxCheckUrl()
|
| 34 |
-
{
|
| 35 |
-
return Mage::helper('adminhtml')->getUrl('adminhtml/abandonedcarts/send');
|
| 36 |
-
}
|
| 37 |
-
|
| 38 |
-
/**
|
| 39 |
-
* Generate button html
|
| 40 |
-
*
|
| 41 |
-
* @return string
|
| 42 |
-
*/
|
| 43 |
-
public function getButtonHtml()
|
| 44 |
-
{
|
| 45 |
-
$button = $this->getLayout()->createBlock('adminhtml/widget_button')
|
| 46 |
-
->setData(array(
|
| 47 |
-
'id' => 'abandonedcarts_button',
|
| 48 |
-
'label' => $this->helper('adminhtml')->__('Send'),
|
| 49 |
-
'onclick' => 'javascript:send(); return false;'
|
| 50 |
-
));
|
| 51 |
-
|
| 52 |
-
return $button->toHtml();
|
| 53 |
-
}
|
| 54 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/code/community/DigitalPianism/Abandonedcarts/Helper/Data.php
CHANGED
|
@@ -6,54 +6,86 @@
|
|
| 6 |
class DigitalPianism_Abandonedcarts_Helper_Data extends Mage_Core_Helper_Abstract
|
| 7 |
{
|
| 8 |
protected $logFileName = 'digitalpianism_abandonedcarts.log';
|
| 9 |
-
|
| 10 |
/**
|
| 11 |
* Log data
|
| 12 |
* @param string|object|array data to log
|
| 13 |
*/
|
| 14 |
-
public function log($data)
|
| 15 |
{
|
| 16 |
Mage::log($data, null, $this->logFileName);
|
| 17 |
}
|
| 18 |
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
{
|
| 24 |
-
return Mage::
|
| 25 |
}
|
| 26 |
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
{
|
| 32 |
-
return Mage::
|
| 33 |
}
|
| 34 |
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
{
|
| 40 |
-
return Mage::
|
| 41 |
}
|
| 42 |
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
{
|
| 48 |
-
return Mage::getStoreConfig('abandonedcartsconfig/
|
| 49 |
}
|
| 50 |
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
{
|
| 56 |
return explode(',',Mage::getStoreConfig('abandonedcartsconfig/options/customer_groups'));
|
| 57 |
}
|
| 58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
}
|
| 6 |
class DigitalPianism_Abandonedcarts_Helper_Data extends Mage_Core_Helper_Abstract
|
| 7 |
{
|
| 8 |
protected $logFileName = 'digitalpianism_abandonedcarts.log';
|
| 9 |
+
|
| 10 |
/**
|
| 11 |
* Log data
|
| 12 |
* @param string|object|array data to log
|
| 13 |
*/
|
| 14 |
+
public function log($data)
|
| 15 |
{
|
| 16 |
Mage::log($data, null, $this->logFileName);
|
| 17 |
}
|
| 18 |
|
| 19 |
+
/**
|
| 20 |
+
* @return mixed
|
| 21 |
+
*/
|
| 22 |
+
public function isEnabled()
|
| 23 |
{
|
| 24 |
+
return Mage::getStoreConfigFlag('abandonedcartsconfig/options/enable');
|
| 25 |
}
|
| 26 |
|
| 27 |
+
/**
|
| 28 |
+
* @return mixed
|
| 29 |
+
*/
|
| 30 |
+
public function isSaleEnabled()
|
| 31 |
{
|
| 32 |
+
return Mage::getStoreConfigFlag('abandonedcartsconfig/options/enable_sale');
|
| 33 |
}
|
| 34 |
|
| 35 |
+
/**
|
| 36 |
+
* @return mixed
|
| 37 |
+
*/
|
| 38 |
+
public function getDryRun()
|
| 39 |
{
|
| 40 |
+
return Mage::getStoreConfigFlag('abandonedcartsconfig/test/dryrun');
|
| 41 |
}
|
| 42 |
|
| 43 |
+
/**
|
| 44 |
+
* @return mixed
|
| 45 |
+
*/
|
| 46 |
+
public function getTestEmail()
|
| 47 |
{
|
| 48 |
+
return Mage::getStoreConfig('abandonedcartsconfig/test/testemail');
|
| 49 |
}
|
| 50 |
|
| 51 |
+
/**
|
| 52 |
+
* @return mixed
|
| 53 |
+
*/
|
| 54 |
+
public function getCustomerGroupsLimitation()
|
| 55 |
{
|
| 56 |
return explode(',',Mage::getStoreConfig('abandonedcartsconfig/options/customer_groups'));
|
| 57 |
}
|
| 58 |
|
| 59 |
+
/**
|
| 60 |
+
* @return bool
|
| 61 |
+
*/
|
| 62 |
+
public function isCampaignEnabled()
|
| 63 |
+
{
|
| 64 |
+
return Mage::getStoreConfigFlag('abandonedcartsconfig/campaign/enable');
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
/**
|
| 68 |
+
* @return mixed
|
| 69 |
+
*/
|
| 70 |
+
public function getCampaignName()
|
| 71 |
+
{
|
| 72 |
+
return Mage::getStoreConfig('abandonedcartsconfig/campaign/name');
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
/**
|
| 76 |
+
* @return bool
|
| 77 |
+
*/
|
| 78 |
+
public function isAutologin()
|
| 79 |
+
{
|
| 80 |
+
return Mage::getStoreConfigFlag('abandonedcartsconfig/email/autologin');
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
/**
|
| 84 |
+
* @return bool
|
| 85 |
+
*/
|
| 86 |
+
public function isLogEnabled()
|
| 87 |
+
{
|
| 88 |
+
return Mage::getStoreConfigFlag('abandonedcartsconfig/test/log');
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
}
|
app/code/community/DigitalPianism/Abandonedcarts/Model/Adminhtml/Observer.php
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Class DigitalPianism_Abandonedcarts_Model_Adminhtml_Observer
|
| 5 |
+
*/
|
| 6 |
+
class DigitalPianism_Abandonedcarts_Model_Adminhtml_Observer {
|
| 7 |
+
|
| 8 |
+
/**
|
| 9 |
+
* @param Varien_Event_Observer $observer
|
| 10 |
+
* @return $this
|
| 11 |
+
*/
|
| 12 |
+
public function registerController(Varien_Event_Observer $observer)
|
| 13 |
+
{
|
| 14 |
+
$action = $observer->getControllerAction()->getFullActionName();
|
| 15 |
+
|
| 16 |
+
switch ($action)
|
| 17 |
+
{
|
| 18 |
+
case 'adminhtml_report_shopcart_abandoned':
|
| 19 |
+
case 'adminhtml_report_shopcart_exportAbandonedCsv':
|
| 20 |
+
case 'adminhtml_report_shopcart_exportAbandonedExcel':
|
| 21 |
+
Mage::register('abandonedcart_report', true);
|
| 22 |
+
break;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
return $this;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
/**
|
| 29 |
+
* @param Varien_Event_Observer $observer
|
| 30 |
+
*/
|
| 31 |
+
public function addExtraColumnsToGrid(Varien_Event_Observer $observer)
|
| 32 |
+
{
|
| 33 |
+
// Get the block
|
| 34 |
+
$block = $observer->getBlock();
|
| 35 |
+
|
| 36 |
+
if ($block instanceof Mage_Adminhtml_Block_Report_Shopcart_Abandoned_Grid) {
|
| 37 |
+
$block->addColumnAfter(
|
| 38 |
+
'abandoned_notified',
|
| 39 |
+
array(
|
| 40 |
+
'header' => Mage::helper('abandonedcarts')->__('Abandoned cart email sent'),
|
| 41 |
+
'index' => 'abandoned_notified',
|
| 42 |
+
'type' => 'options',
|
| 43 |
+
'options' => array(
|
| 44 |
+
0 => Mage::helper('abandonedcarts')->__('No'),
|
| 45 |
+
1 => Mage::helper('abandonedcarts')->__('Yes')
|
| 46 |
+
)
|
| 47 |
+
),
|
| 48 |
+
'remote_ip'
|
| 49 |
+
);
|
| 50 |
+
|
| 51 |
+
$block->addColumnAfter(
|
| 52 |
+
'abandoned_sale_notified',
|
| 53 |
+
array(
|
| 54 |
+
'header' => Mage::helper('abandonedcarts')->__('Abandoned cart sale email sent'),
|
| 55 |
+
'index' => 'abandoned_sale_notified',
|
| 56 |
+
'type' => 'options',
|
| 57 |
+
'options' => array(
|
| 58 |
+
0 => Mage::helper('abandonedcarts')->__('No'),
|
| 59 |
+
1 => Mage::helper('abandonedcarts')->__('Yes')
|
| 60 |
+
)
|
| 61 |
+
),
|
| 62 |
+
'abandoned_notified'
|
| 63 |
+
);
|
| 64 |
+
}
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
/**
|
| 68 |
+
* @param Varien_Event_Observer $observer
|
| 69 |
+
*/
|
| 70 |
+
public function addExtraColumnsToCollection(Varien_Event_Observer $observer)
|
| 71 |
+
{
|
| 72 |
+
// Get the collection
|
| 73 |
+
$collection = $observer->getCollection();
|
| 74 |
+
|
| 75 |
+
if ($collection instanceof Mage_Reports_Model_Resource_Quote_Collection
|
| 76 |
+
&& Mage::registry('abandonedcart_report')) {
|
| 77 |
+
// Add the extra fields
|
| 78 |
+
// Using columns() instead of addFieldToSelect seems to fix the ambiguous column error
|
| 79 |
+
$collection->getSelect()->columns(
|
| 80 |
+
array(
|
| 81 |
+
'abandoned_notified' => 'main_table.abandoned_notified',
|
| 82 |
+
'abandoned_sale_notified' => 'main_table.abandoned_sale_notified'
|
| 83 |
+
)
|
| 84 |
+
);
|
| 85 |
+
}
|
| 86 |
+
}
|
| 87 |
+
}
|
app/code/community/DigitalPianism/Abandonedcarts/Model/Collection.php
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Class DigitalPianism_Abandonedcarts_Model_Collection
|
| 5 |
+
*/
|
| 6 |
+
class DigitalPianism_Abandonedcarts_Model_Collection {
|
| 7 |
+
|
| 8 |
+
/**
|
| 9 |
+
* @param $delay
|
| 10 |
+
* @param $storeId
|
| 11 |
+
* @param $websiteId
|
| 12 |
+
* @param $emails
|
| 13 |
+
* @return mixed
|
| 14 |
+
*/
|
| 15 |
+
public function getCollection($delay, $storeId, $websiteId, $emails = array())
|
| 16 |
+
{
|
| 17 |
+
// Get the product collection
|
| 18 |
+
$collection = Mage::getResourceModel('catalog/product_collection')->setStore($storeId);
|
| 19 |
+
|
| 20 |
+
// Get the attribute id for the status attribute
|
| 21 |
+
$eavAttribute = Mage::getModel('eav/entity_attribute');
|
| 22 |
+
$statusId = $eavAttribute->getIdByCode('catalog_product', 'status');
|
| 23 |
+
$nameId = $eavAttribute->getIdByCode('catalog_product', 'name');
|
| 24 |
+
$priceId = $eavAttribute->getIdByCode('catalog_product', 'price');
|
| 25 |
+
|
| 26 |
+
// Normal join condition
|
| 27 |
+
$emailJoin = sprintf('quote_items.quote_id = quote_table.entity_id AND quote_table.items_count > 0 AND quote_table.is_active = 1 AND quote_table.customer_email IS NOT NULL AND quote_table.abandoned_notified = 0 AND quote_table.updated_at < "%s" AND quote_table.store_id = %s', $delay, $storeId);
|
| 28 |
+
|
| 29 |
+
// In case an array of emails has been specified
|
| 30 |
+
if (!empty($emails)) {
|
| 31 |
+
$emailJoin = sprintf('%s AND quote_table.customer_email IN (%s)', $emailJoin, '"' . implode('", "', $emails) . '"');
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
// If flat catalog is enabled
|
| 35 |
+
if (Mage::helper('catalog/product_flat')->isEnabled())
|
| 36 |
+
{
|
| 37 |
+
// First collection: carts with products that became on sale
|
| 38 |
+
// Join the collection with the required tables
|
| 39 |
+
$collection->getSelect()
|
| 40 |
+
->reset(Zend_Db_Select::COLUMNS)
|
| 41 |
+
->columns(array('e.entity_id AS product_id',
|
| 42 |
+
'e.sku',
|
| 43 |
+
'catalog_flat.name as product_name',
|
| 44 |
+
'catalog_flat.price as product_price',
|
| 45 |
+
'quote_table.entity_id as cart_id',
|
| 46 |
+
'quote_table.updated_at as cart_updated_at',
|
| 47 |
+
'quote_table.abandoned_notified as has_been_notified',
|
| 48 |
+
'quote_table.customer_email as customer_email',
|
| 49 |
+
'quote_table.customer_firstname as customer_firstname',
|
| 50 |
+
'quote_table.customer_lastname as customer_lastname',
|
| 51 |
+
'customer.group_id as customer_group'
|
| 52 |
+
)
|
| 53 |
+
)
|
| 54 |
+
->joinInner(
|
| 55 |
+
array('quote_items' => Mage::getSingleton("core/resource")->getTableName('sales_flat_quote_item')),
|
| 56 |
+
'quote_items.product_id = e.entity_id AND quote_items.price > 0.00',
|
| 57 |
+
null)
|
| 58 |
+
->joinInner(
|
| 59 |
+
array('quote_table' => Mage::getSingleton("core/resource")->getTableName('sales_flat_quote')),
|
| 60 |
+
$emailJoin,
|
| 61 |
+
null)
|
| 62 |
+
->joinInner(
|
| 63 |
+
array('catalog_flat' => Mage::getSingleton("core/resource")->getTableName('catalog_product_flat_'.$storeId)),
|
| 64 |
+
'catalog_flat.entity_id = e.entity_id',
|
| 65 |
+
null)
|
| 66 |
+
->joinInner(
|
| 67 |
+
array('catalog_enabled' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_int')),
|
| 68 |
+
'catalog_enabled.entity_id = e.entity_id AND catalog_enabled.attribute_id = '.$statusId.' AND catalog_enabled.value = 1',
|
| 69 |
+
null)
|
| 70 |
+
->joinInner(
|
| 71 |
+
array('inventory' => Mage::getSingleton("core/resource")->getTableName('cataloginventory_stock_status')),
|
| 72 |
+
'inventory.product_id = e.entity_id AND inventory.stock_status = 1 AND website_id = '.$websiteId,
|
| 73 |
+
null)
|
| 74 |
+
->joinInner(
|
| 75 |
+
array('customer' => Mage::getSingleton("core/resource")->getTableName('customer_entity')),
|
| 76 |
+
'quote_table.customer_email = customer.email',
|
| 77 |
+
null)
|
| 78 |
+
->order('quote_table.updated_at DESC');
|
| 79 |
+
}
|
| 80 |
+
else
|
| 81 |
+
{
|
| 82 |
+
// First collection: carts with products that became on sale
|
| 83 |
+
// Join the collection with the required tables
|
| 84 |
+
$collection->getSelect()
|
| 85 |
+
->reset(Zend_Db_Select::COLUMNS)
|
| 86 |
+
->columns(array('e.entity_id AS product_id',
|
| 87 |
+
'e.sku',
|
| 88 |
+
'catalog_name.value as product_name',
|
| 89 |
+
'catalog_price.value as product_price',
|
| 90 |
+
'quote_table.entity_id as cart_id',
|
| 91 |
+
'quote_table.updated_at as cart_updated_at',
|
| 92 |
+
'quote_table.abandoned_notified as has_been_notified',
|
| 93 |
+
'quote_table.customer_email as customer_email',
|
| 94 |
+
'quote_table.customer_firstname as customer_firstname',
|
| 95 |
+
'quote_table.customer_lastname as customer_lastname',
|
| 96 |
+
'customer.group_id as customer_group'
|
| 97 |
+
)
|
| 98 |
+
)
|
| 99 |
+
// Name
|
| 100 |
+
->joinInner(
|
| 101 |
+
array('catalog_name' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_varchar')),
|
| 102 |
+
"catalog_name.entity_id = e.entity_id AND catalog_name.attribute_id = $nameId",
|
| 103 |
+
null)
|
| 104 |
+
// Price
|
| 105 |
+
->joinInner(
|
| 106 |
+
array('catalog_price' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_decimal')),
|
| 107 |
+
"catalog_price.entity_id = e.entity_id AND catalog_price.attribute_id = $priceId",
|
| 108 |
+
null)
|
| 109 |
+
->joinInner(
|
| 110 |
+
array('quote_items' => Mage::getSingleton("core/resource")->getTableName('sales_flat_quote_item')),
|
| 111 |
+
'quote_items.product_id = e.entity_id AND quote_items.price > 0.00',
|
| 112 |
+
null)
|
| 113 |
+
->joinInner(
|
| 114 |
+
array('quote_table' => Mage::getSingleton("core/resource")->getTableName('sales_flat_quote')),
|
| 115 |
+
$emailJoin,
|
| 116 |
+
null)
|
| 117 |
+
->joinInner(
|
| 118 |
+
array('catalog_enabled' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_int')),
|
| 119 |
+
'catalog_enabled.entity_id = e.entity_id AND catalog_enabled.attribute_id = '.$statusId.' AND catalog_enabled.value = 1',
|
| 120 |
+
null)
|
| 121 |
+
->joinInner(
|
| 122 |
+
array('inventory' => Mage::getSingleton("core/resource")->getTableName('cataloginventory_stock_status')),
|
| 123 |
+
'inventory.product_id = e.entity_id AND inventory.stock_status = 1 AND website_id = '.$websiteId,
|
| 124 |
+
null)
|
| 125 |
+
->joinInner(
|
| 126 |
+
array('customer' => Mage::getSingleton("core/resource")->getTableName('customer_entity')),
|
| 127 |
+
'quote_table.customer_email = customer.email',
|
| 128 |
+
null)
|
| 129 |
+
->order('quote_table.updated_at DESC');
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
return $collection;
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
public function getSalesCollection($storeId, $websiteId, $emails = array())
|
| 136 |
+
{
|
| 137 |
+
// Get the product collection
|
| 138 |
+
$collection = Mage::getResourceModel('catalog/product_collection')->setStore($storeId);
|
| 139 |
+
|
| 140 |
+
// Get the attribute id for the status attribute
|
| 141 |
+
$eavAttribute = Mage::getModel('eav/entity_attribute');
|
| 142 |
+
$statusId = $eavAttribute->getIdByCode('catalog_product', 'status');
|
| 143 |
+
$nameId = $eavAttribute->getIdByCode('catalog_product', 'name');
|
| 144 |
+
$priceId = $eavAttribute->getIdByCode('catalog_product', 'price');
|
| 145 |
+
$spriceId = $eavAttribute->getIdByCode('catalog_product', 'special_price');
|
| 146 |
+
$spfromId = $eavAttribute->getIdByCode('catalog_product', 'special_from_date');
|
| 147 |
+
$sptoId = $eavAttribute->getIdByCode('catalog_product', 'special_to_date');
|
| 148 |
+
|
| 149 |
+
// Normal join condition
|
| 150 |
+
$emailJoin = sprintf('quote_items.quote_id = quote_table.entity_id AND quote_table.items_count > 0 AND quote_table.is_active = 1 AND quote_table.customer_email IS NOT NULL AND quote_table.abandoned_sale_notified = 0 AND quote_table.store_id = %s', $storeId);
|
| 151 |
+
|
| 152 |
+
// In case an array of emails has been specified
|
| 153 |
+
if (!empty($emails)) {
|
| 154 |
+
$emailJoin = sprintf('%s AND quote_table.customer_email IN (%s)', $emailJoin, '"' . implode('", "', $emails) . '"');
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
// If flat catalog is enabled
|
| 158 |
+
if (Mage::helper('catalog/product_flat')->isEnabled())
|
| 159 |
+
{
|
| 160 |
+
// First collection: carts with products that became on sale
|
| 161 |
+
// Join the collection with the required tables
|
| 162 |
+
$collection->getSelect()
|
| 163 |
+
->reset(Zend_Db_Select::COLUMNS)
|
| 164 |
+
->columns(array('e.entity_id AS product_id',
|
| 165 |
+
'e.sku',
|
| 166 |
+
'catalog_flat.name as product_name',
|
| 167 |
+
'catalog_flat.price as product_price',
|
| 168 |
+
'catalog_flat.special_price as product_special_price',
|
| 169 |
+
'catalog_flat.special_from_date as product_special_from_date',
|
| 170 |
+
'catalog_flat.special_to_date as product_special_to_date',
|
| 171 |
+
'quote_table.entity_id as cart_id',
|
| 172 |
+
'quote_table.updated_at as cart_updated_at',
|
| 173 |
+
'quote_table.abandoned_sale_notified as has_been_notified',
|
| 174 |
+
'quote_items.price as product_price_in_cart',
|
| 175 |
+
'quote_table.customer_email as customer_email',
|
| 176 |
+
'quote_table.customer_firstname as customer_firstname',
|
| 177 |
+
'quote_table.customer_lastname as customer_lastname',
|
| 178 |
+
'customer.group_id as customer_group'
|
| 179 |
+
)
|
| 180 |
+
)
|
| 181 |
+
->joinInner(
|
| 182 |
+
array('quote_items' => Mage::getSingleton("core/resource")->getTableName('sales_flat_quote_item')),
|
| 183 |
+
'quote_items.product_id = e.entity_id AND quote_items.price > 0.00',
|
| 184 |
+
null)
|
| 185 |
+
->joinInner(
|
| 186 |
+
array('quote_table' => Mage::getSingleton("core/resource")->getTableName('sales_flat_quote')),
|
| 187 |
+
$emailJoin,
|
| 188 |
+
null)
|
| 189 |
+
->joinInner(
|
| 190 |
+
array('catalog_flat' => Mage::getSingleton("core/resource")->getTableName('catalog_product_flat_'.$storeId)),
|
| 191 |
+
'catalog_flat.entity_id = e.entity_id',
|
| 192 |
+
null)
|
| 193 |
+
->joinInner(
|
| 194 |
+
array('catalog_enabled' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_int')),
|
| 195 |
+
'catalog_enabled.entity_id = e.entity_id AND catalog_enabled.attribute_id = '.$statusId.' AND catalog_enabled.value = 1',
|
| 196 |
+
null)
|
| 197 |
+
->joinInner(
|
| 198 |
+
array('inventory' => Mage::getSingleton("core/resource")->getTableName('cataloginventory_stock_status')),
|
| 199 |
+
'inventory.product_id = e.entity_id AND inventory.stock_status = 1 AND inventory.website_id = '.$websiteId,
|
| 200 |
+
null)
|
| 201 |
+
->joinInner(
|
| 202 |
+
array('customer' => Mage::getSingleton("core/resource")->getTableName('customer_entity')),
|
| 203 |
+
'quote_table.customer_email = customer.email',
|
| 204 |
+
null)
|
| 205 |
+
->order('quote_table.updated_at DESC');
|
| 206 |
+
}
|
| 207 |
+
else
|
| 208 |
+
{
|
| 209 |
+
// First collection: carts with products that became on sale
|
| 210 |
+
// Join the collection with the required tables
|
| 211 |
+
$collection->getSelect()
|
| 212 |
+
->reset(Zend_Db_Select::COLUMNS)
|
| 213 |
+
->columns(array('e.entity_id AS product_id',
|
| 214 |
+
'e.sku',
|
| 215 |
+
'catalog_name.value as product_name',
|
| 216 |
+
'catalog_price.value as product_price',
|
| 217 |
+
'catalog_sprice.value as product_special_price',
|
| 218 |
+
'catalog_spfrom.value as product_special_from_date',
|
| 219 |
+
'catalog_spto.value as product_special_to_date',
|
| 220 |
+
'quote_table.entity_id as cart_id',
|
| 221 |
+
'quote_table.updated_at as cart_updated_at',
|
| 222 |
+
'quote_table.abandoned_sale_notified as has_been_notified',
|
| 223 |
+
'quote_items.price as product_price_in_cart',
|
| 224 |
+
'quote_table.customer_email as customer_email',
|
| 225 |
+
'quote_table.customer_firstname as customer_firstname',
|
| 226 |
+
'quote_table.customer_lastname as customer_lastname',
|
| 227 |
+
'customer.group_id as customer_group'
|
| 228 |
+
)
|
| 229 |
+
)
|
| 230 |
+
// Name
|
| 231 |
+
->joinInner(
|
| 232 |
+
array('catalog_name' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_varchar')),
|
| 233 |
+
"catalog_name.entity_id = e.entity_id AND catalog_name.attribute_id = $nameId",
|
| 234 |
+
null)
|
| 235 |
+
// Price
|
| 236 |
+
->joinInner(
|
| 237 |
+
array('catalog_price' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_decimal')),
|
| 238 |
+
"catalog_price.entity_id = e.entity_id AND catalog_price.attribute_id = $priceId",
|
| 239 |
+
null)
|
| 240 |
+
// Special Price
|
| 241 |
+
->joinInner(
|
| 242 |
+
array('catalog_sprice' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_decimal')),
|
| 243 |
+
"catalog_sprice.entity_id = e.entity_id AND catalog_sprice.attribute_id = $spriceId",
|
| 244 |
+
null)
|
| 245 |
+
// Special From Date
|
| 246 |
+
->joinInner(
|
| 247 |
+
array('catalog_spfrom' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_datetime')),
|
| 248 |
+
"catalog_spfrom.entity_id = e.entity_id AND catalog_spfrom.attribute_id = $spfromId",
|
| 249 |
+
null)
|
| 250 |
+
// Special To Date
|
| 251 |
+
->joinInner(
|
| 252 |
+
array('catalog_spto' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_datetime')),
|
| 253 |
+
"catalog_spto.entity_id = e.entity_id AND catalog_spto.attribute_id = $sptoId",
|
| 254 |
+
null)
|
| 255 |
+
->joinInner(
|
| 256 |
+
array('quote_items' => Mage::getSingleton("core/resource")->getTableName('sales_flat_quote_item')),
|
| 257 |
+
'quote_items.product_id = e.entity_id AND quote_items.price > 0.00',
|
| 258 |
+
null)
|
| 259 |
+
->joinInner(
|
| 260 |
+
array('quote_table' => Mage::getSingleton("core/resource")->getTableName('sales_flat_quote')),
|
| 261 |
+
$emailJoin,
|
| 262 |
+
null)
|
| 263 |
+
->joinInner(
|
| 264 |
+
array('catalog_enabled' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_int')),
|
| 265 |
+
'catalog_enabled.entity_id = e.entity_id AND catalog_enabled.attribute_id = '.$statusId.' AND catalog_enabled.value = 1',
|
| 266 |
+
null)
|
| 267 |
+
->joinInner(
|
| 268 |
+
array('inventory' => Mage::getSingleton("core/resource")->getTableName('cataloginventory_stock_status')),
|
| 269 |
+
'inventory.product_id = e.entity_id AND inventory.stock_status = 1 AND inventory.website_id = '.$websiteId,
|
| 270 |
+
null)
|
| 271 |
+
->joinInner(
|
| 272 |
+
array('customer' => Mage::getSingleton("core/resource")->getTableName('customer_entity')),
|
| 273 |
+
'quote_table.customer_email = customer.email',
|
| 274 |
+
null)
|
| 275 |
+
->order('quote_table.updated_at DESC');
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
return $collection;
|
| 279 |
+
}
|
| 280 |
+
}
|
app/code/community/DigitalPianism/Abandonedcarts/Model/Link.php
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Class DigitalPianism_Abandonedcarts_Model_Link
|
| 5 |
+
*/
|
| 6 |
+
class DigitalPianism_Abandonedcarts_Model_Link extends Mage_Core_Model_Abstract
|
| 7 |
+
{
|
| 8 |
+
|
| 9 |
+
protected function _construct()
|
| 10 |
+
{
|
| 11 |
+
$this->_init('abandonedcarts/link', 'link_id');
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
}
|
app/code/community/DigitalPianism/Abandonedcarts/Model/Link/Cleaner.php
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Class DigitalPianism_Abandonedcarts_Model_Link_Cleaner
|
| 5 |
+
*/
|
| 6 |
+
class DigitalPianism_Abandonedcarts_Model_Link_Cleaner {
|
| 7 |
+
|
| 8 |
+
/**
|
| 9 |
+
*
|
| 10 |
+
*/
|
| 11 |
+
public function cleanExpiredLinks()
|
| 12 |
+
{
|
| 13 |
+
$now = new Zend_Date(Mage::getModel('core/date')->timestamp());
|
| 14 |
+
|
| 15 |
+
// Get the collection of links expired
|
| 16 |
+
$collection = Mage::getResourceModel('abandonedcarts/link_collection')
|
| 17 |
+
->addFieldToSelect('link_id')
|
| 18 |
+
->addFieldToFilter('expiration_date', array(
|
| 19 |
+
'lteq' => $now->toString('YYYY-MM-dd HH:mm:ss')
|
| 20 |
+
)
|
| 21 |
+
);
|
| 22 |
+
|
| 23 |
+
if (!$collection->getSize())
|
| 24 |
+
return;
|
| 25 |
+
|
| 26 |
+
// Delete the expired links
|
| 27 |
+
foreach ($collection as $expiredLink) {
|
| 28 |
+
$expiredLink->delete();
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
+
}
|
app/code/community/DigitalPianism/Abandonedcarts/Model/Log.php
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Class DigitalPianism_Abandonedcarts_Model_Log
|
| 5 |
+
*/
|
| 6 |
+
class DigitalPianism_Abandonedcarts_Model_Log extends Mage_Core_Model_Abstract
|
| 7 |
+
{
|
| 8 |
+
|
| 9 |
+
const TYPE_NORMAL = 0;
|
| 10 |
+
const TYPE_SALES = 1;
|
| 11 |
+
|
| 12 |
+
protected function _construct()
|
| 13 |
+
{
|
| 14 |
+
$this->_init('abandonedcarts/log', 'log_id');
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
public function toOptionArray()
|
| 18 |
+
{
|
| 19 |
+
return array(
|
| 20 |
+
self::TYPE_NORMAL => Mage::helper('abandonedcarts')->__('Abandoned cart email'),
|
| 21 |
+
self::TYPE_SALES => Mage::helper('abandonedcarts')->__('Sale abandoned cart email')
|
| 22 |
+
);
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
}
|
app/code/community/DigitalPianism/Abandonedcarts/Model/Notifier.php
ADDED
|
@@ -0,0 +1,637 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Class DigitalPianism_Abandonedcarts_Model_Notifier
|
| 5 |
+
*/
|
| 6 |
+
class DigitalPianism_Abandonedcarts_Model_Notifier extends Mage_Core_Model_Abstract
|
| 7 |
+
{
|
| 8 |
+
const IMAGE_SIZE = 250;
|
| 9 |
+
const CAMPAIGN_SOURCE = "abandonedcarts";
|
| 10 |
+
const CAMPAIGN_MEDIUM = "email";
|
| 11 |
+
/**
|
| 12 |
+
* Autologin links expiration in days
|
| 13 |
+
*/
|
| 14 |
+
const EXPIRATION = "2";
|
| 15 |
+
|
| 16 |
+
/**
|
| 17 |
+
* @var array
|
| 18 |
+
*/
|
| 19 |
+
protected $_recipients = array();
|
| 20 |
+
|
| 21 |
+
/**
|
| 22 |
+
* @var array
|
| 23 |
+
*/
|
| 24 |
+
protected $_saleRecipients = array();
|
| 25 |
+
|
| 26 |
+
/**
|
| 27 |
+
* @var string
|
| 28 |
+
*/
|
| 29 |
+
protected $_today = "";
|
| 30 |
+
|
| 31 |
+
/**
|
| 32 |
+
* @var array
|
| 33 |
+
*/
|
| 34 |
+
protected $_customerGroups = array();
|
| 35 |
+
|
| 36 |
+
/**
|
| 37 |
+
* @var
|
| 38 |
+
*/
|
| 39 |
+
protected $_currentStoreId;
|
| 40 |
+
|
| 41 |
+
/**
|
| 42 |
+
* @var
|
| 43 |
+
*/
|
| 44 |
+
protected $_originalStoreId;
|
| 45 |
+
|
| 46 |
+
/**
|
| 47 |
+
* @throws Zend_Date_Exception
|
| 48 |
+
*/
|
| 49 |
+
protected function _setToday()
|
| 50 |
+
{
|
| 51 |
+
// Date handling
|
| 52 |
+
$store = Mage_Core_Model_App::ADMIN_STORE_ID;
|
| 53 |
+
$timezone = Mage::app()->getStore($store)->getConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_TIMEZONE);
|
| 54 |
+
date_default_timezone_set($timezone);
|
| 55 |
+
|
| 56 |
+
// Current date
|
| 57 |
+
$currentdate = date("Ymd");
|
| 58 |
+
|
| 59 |
+
$day = (int)substr($currentdate,-2);
|
| 60 |
+
$month = (int)substr($currentdate,4,2);
|
| 61 |
+
$year = (int)substr($currentdate,0,4);
|
| 62 |
+
|
| 63 |
+
$date = array(
|
| 64 |
+
'year' => $year,
|
| 65 |
+
'month' => $month,
|
| 66 |
+
'day' => $day,
|
| 67 |
+
'hour' => 23,
|
| 68 |
+
'minute' => 59,
|
| 69 |
+
'second' => 59
|
| 70 |
+
);
|
| 71 |
+
|
| 72 |
+
$today = new Zend_Date($date);
|
| 73 |
+
$today->setTimeZone("UTC");
|
| 74 |
+
|
| 75 |
+
date_default_timezone_set($timezone);
|
| 76 |
+
|
| 77 |
+
$this->_today = $today->toString("Y-MM-dd HH:mm:ss");
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
/**
|
| 81 |
+
* @return string
|
| 82 |
+
*/
|
| 83 |
+
protected function _getToday()
|
| 84 |
+
{
|
| 85 |
+
return $this->_today;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
/**
|
| 89 |
+
* @return array
|
| 90 |
+
*/
|
| 91 |
+
protected function _getRecipients()
|
| 92 |
+
{
|
| 93 |
+
return $this->_recipients;
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
/**
|
| 97 |
+
* @return array
|
| 98 |
+
*/
|
| 99 |
+
protected function _getSaleRecipients()
|
| 100 |
+
{
|
| 101 |
+
return $this->_saleRecipients;
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
protected function _getProductImage($productId)
|
| 105 |
+
{
|
| 106 |
+
// Get product image via collection
|
| 107 |
+
$_productCollection = Mage::getResourceModel('catalog/product_collection');
|
| 108 |
+
// Add attributes to the collection
|
| 109 |
+
$_productCollection->addAttributeToFilter('entity_id',array('eq' => $productId));
|
| 110 |
+
// Add image to the collection
|
| 111 |
+
$_productCollection->joinAttribute('small_image', 'catalog_product/image', 'entity_id', null, 'left');
|
| 112 |
+
// Limit the collection to get the specific product
|
| 113 |
+
$_productCollection->setPageSize(1);
|
| 114 |
+
|
| 115 |
+
try {
|
| 116 |
+
$productImg = (string)Mage::helper('catalog/image')->init($_productCollection->getFirstItem(), 'small_image')->resize(self::IMAGE_SIZE);
|
| 117 |
+
} catch (Exception $e) {
|
| 118 |
+
$productImg = false;
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
return $productImg;
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
/**
|
| 125 |
+
* @param $args
|
| 126 |
+
*/
|
| 127 |
+
public function generateRecipients($args)
|
| 128 |
+
{
|
| 129 |
+
// Customer group check
|
| 130 |
+
if (array_key_exists('customer_group',$args['row'])
|
| 131 |
+
&& !in_array($args['row']['customer_group'],$this->_customerGroups)) {
|
| 132 |
+
return;
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
// Test if the customer is already in the array
|
| 136 |
+
if (!array_key_exists($args['row']['customer_email'], $this->_recipients)) {
|
| 137 |
+
// Create an array of variables to assign to template
|
| 138 |
+
$emailTemplateVariables = array();
|
| 139 |
+
|
| 140 |
+
// Array that contains the data which will be used inside the template
|
| 141 |
+
$emailTemplateVariables['fullname'] = $args['row']['customer_firstname'].' '.$args['row']['customer_lastname'];
|
| 142 |
+
$emailTemplateVariables['firstname'] = $args['row']['customer_firstname'];
|
| 143 |
+
$emailTemplateVariables['productname'][] = $args['row']['product_name'];
|
| 144 |
+
|
| 145 |
+
// Assign the values to the array of recipients
|
| 146 |
+
$this->_recipients[$args['row']['customer_email']]['cartId'] = $args['row']['cart_id'];
|
| 147 |
+
|
| 148 |
+
// Add product image
|
| 149 |
+
$emailTemplateVariables['productimage'][] = $this->_getProductImage($args['row']['product_id']);
|
| 150 |
+
|
| 151 |
+
// Add the link
|
| 152 |
+
$token = "";
|
| 153 |
+
// Autologin only applies to real customer (skip not logged in customer group)
|
| 154 |
+
if (Mage::helper('abandonedcarts')->isAutologin()) {
|
| 155 |
+
$token = $this->_generateToken($args['row']['customer_email']);
|
| 156 |
+
}
|
| 157 |
+
$emailTemplateVariables['link'] = $this->_generateUrl($token);
|
| 158 |
+
} else {
|
| 159 |
+
// We create some extra variables if there is several products in the cart
|
| 160 |
+
$emailTemplateVariables = $this->_recipients[$args['row']['customer_email']]['emailTemplateVariables'];
|
| 161 |
+
// We increase the product count
|
| 162 |
+
//$emailTemplateVariables['extraproductcount'] += 1;
|
| 163 |
+
$emailTemplateVariables['productname'][] = $args['row']['product_name'];
|
| 164 |
+
|
| 165 |
+
// Add product image
|
| 166 |
+
$emailTemplateVariables['productimage'][] = $this->_getProductImage($args['row']['product_id']);
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
// Assign the array of template variables
|
| 170 |
+
$this->_recipients[$args['row']['customer_email']]['emailTemplateVariables'] = $emailTemplateVariables;
|
| 171 |
+
$this->_recipients[$args['row']['customer_email']]['store_id'] = $this->_currentStoreId;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
/**
|
| 175 |
+
* @param $args
|
| 176 |
+
*/
|
| 177 |
+
public function generateSaleRecipients($args)
|
| 178 |
+
{
|
| 179 |
+
// Customer group check
|
| 180 |
+
if (array_key_exists('customer_group',$args['row'])
|
| 181 |
+
&& !in_array($args['row']['customer_group'],$this->_customerGroups)) {
|
| 182 |
+
|
| 183 |
+
return;
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
// Double check if the special from date is set
|
| 187 |
+
if (!array_key_exists('product_special_from_date',$args['row'])
|
| 188 |
+
|| !$args['row']['product_special_from_date']) {
|
| 189 |
+
|
| 190 |
+
// If not we use today for the comparison
|
| 191 |
+
$fromDate = $this->_getToday();
|
| 192 |
+
} else {
|
| 193 |
+
$fromDate = $args['row']['product_special_from_date'];
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
// Do the same for the special to date
|
| 197 |
+
if (!array_key_exists('product_special_to_date',$args['row'])
|
| 198 |
+
|| !$args['row']['product_special_to_date']) {
|
| 199 |
+
|
| 200 |
+
$toDate = $this->_getToday();
|
| 201 |
+
} else {
|
| 202 |
+
$toDate = $args['row']['product_special_to_date'];
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
// We need to ensure that the price in cart is higher than the new special price
|
| 206 |
+
// As well as the date comparison in case the sale is over or hasn't started
|
| 207 |
+
if ($args['row']['product_price_in_cart'] > 0.00
|
| 208 |
+
&& $args['row']['product_special_price'] > 0.00
|
| 209 |
+
&& ($args['row']['product_price_in_cart'] > $args['row']['product_special_price'])
|
| 210 |
+
&& ($fromDate <= $this->_getToday())
|
| 211 |
+
&& ($toDate >= $this->_getToday())) {
|
| 212 |
+
|
| 213 |
+
// Test if the customer is already in the array
|
| 214 |
+
if (!array_key_exists($args['row']['customer_email'], $this->_saleRecipients)) {
|
| 215 |
+
|
| 216 |
+
// Create an array of variables to assign to template
|
| 217 |
+
$emailTemplateVariables = array();
|
| 218 |
+
|
| 219 |
+
// Array that contains the data which will be used inside the template
|
| 220 |
+
$emailTemplateVariables['fullname'] = $args['row']['customer_firstname'].' '.$args['row']['customer_lastname'];
|
| 221 |
+
$emailTemplateVariables['firstname'] = $args['row']['customer_firstname'];
|
| 222 |
+
$emailTemplateVariables['productname'][] = $args['row']['product_name'];
|
| 223 |
+
$emailTemplateVariables['cartprice'][] = Mage::helper('core')->currency(floatval(number_format(floatval($args['row']['product_price_in_cart']),2)), true, false);
|
| 224 |
+
$emailTemplateVariables['specialprice'][] = Mage::helper('core')->currency(floatval(number_format(floatval($args['row']['product_special_price']),2)), true, false);
|
| 225 |
+
|
| 226 |
+
// Assign the values to the array of recipients
|
| 227 |
+
$this->_saleRecipients[$args['row']['customer_email']]['cartId'] = $args['row']['cart_id'];
|
| 228 |
+
|
| 229 |
+
// Add product image
|
| 230 |
+
$emailTemplateVariables['productimage'][] = $this->_getProductImage($args['row']['product_id']);
|
| 231 |
+
|
| 232 |
+
// Add the link
|
| 233 |
+
$token = "";
|
| 234 |
+
// Autologin only applies to real customer (skip not logged in customer group)
|
| 235 |
+
if (Mage::helper('abandonedcarts')->isAutologin()) {
|
| 236 |
+
$token = $this->_generateToken($args['row']['customer_email']);
|
| 237 |
+
}
|
| 238 |
+
$emailTemplateVariables['link'] = $this->_generateUrl($token);
|
| 239 |
+
|
| 240 |
+
// If one product before
|
| 241 |
+
$emailTemplateVariables['discount'] = number_format(floatval($args['row']['product_price_in_cart']),2) - number_format(floatval($args['row']['product_special_price']),2);
|
| 242 |
+
} else {
|
| 243 |
+
// We create some extra variables if there is several products in the cart
|
| 244 |
+
$emailTemplateVariables = $this->_saleRecipients[$args['row']['customer_email']]['emailTemplateVariables'];
|
| 245 |
+
// Discount amount
|
| 246 |
+
// We add the discount on the second product
|
| 247 |
+
$moreDiscount = number_format(floatval($args['row']['product_price_in_cart']),2) - number_format(floatval($args['row']['product_special_price']),2);
|
| 248 |
+
$emailTemplateVariables['discount'] += $moreDiscount;
|
| 249 |
+
|
| 250 |
+
$emailTemplateVariables['productname'][] = $args['row']['product_name'];
|
| 251 |
+
$emailTemplateVariables['cartprice'][] = Mage::helper('core')->currency(floatval(number_format(floatval($args['row']['product_price_in_cart']),2)), true, false);
|
| 252 |
+
$emailTemplateVariables['specialprice'][] = Mage::helper('core')->currency(floatval(number_format(floatval($args['row']['product_special_price']),2)), true, false);
|
| 253 |
+
|
| 254 |
+
// Add product image
|
| 255 |
+
$emailTemplateVariables['productimage'][] = $this->_getProductImage($args['row']['product_id']);
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
// Assign the array of template variables
|
| 259 |
+
$this->_saleRecipients[$args['row']['customer_email']]['emailTemplateVariables'] = $emailTemplateVariables;
|
| 260 |
+
$this->_saleRecipients[$args['row']['customer_email']]['store_id'] = $this->_currentStoreId;
|
| 261 |
+
}
|
| 262 |
+
}
|
| 263 |
+
|
| 264 |
+
/**
|
| 265 |
+
* @param $dryrun
|
| 266 |
+
* @param $testemail
|
| 267 |
+
* @throws Exception
|
| 268 |
+
*/
|
| 269 |
+
protected function _sendSaleEmails($dryrun, $testemail)
|
| 270 |
+
{
|
| 271 |
+
// Send the emails via a loop
|
| 272 |
+
foreach ($this->_getSaleRecipients() as $email => $recipient) {
|
| 273 |
+
|
| 274 |
+
// Store Id
|
| 275 |
+
Mage::app()->setCurrentStore($recipient['store_id']);
|
| 276 |
+
// Get the transactional email template
|
| 277 |
+
$templateId = Mage::getStoreConfig('abandonedcartsconfig/email/email_template_sale');
|
| 278 |
+
// Get the sender
|
| 279 |
+
$sender = array();
|
| 280 |
+
$sender['email'] = Mage::getStoreConfig('abandonedcartsconfig/email/email');
|
| 281 |
+
$sender['name'] = Mage::getStoreConfig('abandonedcartsconfig/email/name');
|
| 282 |
+
$recipient['emailTemplateVariables']['email'] = $email;
|
| 283 |
+
|
| 284 |
+
// Format discount with currency
|
| 285 |
+
$recipient['emailTemplateVariables']['discount'] = Mage::helper('core')->currency($recipient['emailTemplateVariables']['discount'], true, false);
|
| 286 |
+
|
| 287 |
+
// Don't send the email if dryrun is set
|
| 288 |
+
if ($dryrun) {
|
| 289 |
+
// If the test email is set and found
|
| 290 |
+
if (isset($testemail)) {
|
| 291 |
+
// Send to the test email
|
| 292 |
+
Mage::getModel('core/email_template')
|
| 293 |
+
->sendTransactional(
|
| 294 |
+
$templateId,
|
| 295 |
+
$sender,
|
| 296 |
+
$testemail,
|
| 297 |
+
$recipient['emailTemplateVariables']['fullname'] ,
|
| 298 |
+
$recipient['emailTemplateVariables'],
|
| 299 |
+
null);
|
| 300 |
+
}
|
| 301 |
+
} else {
|
| 302 |
+
// Send the email
|
| 303 |
+
Mage::getModel('core/email_template')
|
| 304 |
+
->sendTransactional(
|
| 305 |
+
$templateId,
|
| 306 |
+
$sender,
|
| 307 |
+
$email,
|
| 308 |
+
$recipient['emailTemplateVariables']['fullname'] ,
|
| 309 |
+
$recipient['emailTemplateVariables'],
|
| 310 |
+
null);
|
| 311 |
+
}
|
| 312 |
+
|
| 313 |
+
if (Mage::helper('abandonedcarts')->isLogEnabled()) {
|
| 314 |
+
// Log the details
|
| 315 |
+
$comment = sprintf(
|
| 316 |
+
"Email sent to %s, product name: %s, cart price: %s, special price: %s, discount: %s, product image: %s, link: %s",
|
| 317 |
+
$recipient['emailTemplateVariables']['fullname'],
|
| 318 |
+
implode(',', $recipient['emailTemplateVariables']['productname']),
|
| 319 |
+
implode(',', $recipient['emailTemplateVariables']['cartprice']),
|
| 320 |
+
implode(',', $recipient['emailTemplateVariables']['specialprice']),
|
| 321 |
+
$recipient['emailTemplateVariables']['discount'],
|
| 322 |
+
implode(',', $recipient['emailTemplateVariables']['productimage']),
|
| 323 |
+
$recipient['emailTemplateVariables']['link']
|
| 324 |
+
);
|
| 325 |
+
|
| 326 |
+
Mage::getModel('abandonedcarts/log')->setData(
|
| 327 |
+
array(
|
| 328 |
+
'customer_email' => $email,
|
| 329 |
+
'type' => DigitalPianism_Abandonedcarts_Model_Log::TYPE_SALES,
|
| 330 |
+
'comment' => $comment,
|
| 331 |
+
'store' => $recipient['store_id'],
|
| 332 |
+
'dryrun' => $dryrun ? 1 : 0
|
| 333 |
+
)
|
| 334 |
+
)->save();
|
| 335 |
+
}
|
| 336 |
+
|
| 337 |
+
// Save only if dryrun is false
|
| 338 |
+
if (!$dryrun) {
|
| 339 |
+
|
| 340 |
+
// Load the quote
|
| 341 |
+
$quote = Mage::getModel('sales/quote')->load($recipient['cartId']);
|
| 342 |
+
|
| 343 |
+
// We change the notification attribute
|
| 344 |
+
$quote->setAbandonedSaleNotified(1);
|
| 345 |
+
|
| 346 |
+
$quote->getResource()->saveAttribute($quote,array('abandoned_sale_notified'));
|
| 347 |
+
}
|
| 348 |
+
}
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
/**
|
| 352 |
+
* @param $dryrun
|
| 353 |
+
* @param $testemail
|
| 354 |
+
* @throws Exception
|
| 355 |
+
*/
|
| 356 |
+
protected function _sendEmails($dryrun, $testemail)
|
| 357 |
+
{
|
| 358 |
+
// Send the emails via a loop
|
| 359 |
+
foreach ($this->_getRecipients() as $email => $recipient) {
|
| 360 |
+
|
| 361 |
+
// Store ID
|
| 362 |
+
Mage::app()->setCurrentStore($recipient['store_id']);
|
| 363 |
+
// Get the transactional email template
|
| 364 |
+
$templateId = Mage::getStoreConfig('abandonedcartsconfig/email/email_template');
|
| 365 |
+
// Get the sender
|
| 366 |
+
$sender = array();
|
| 367 |
+
$sender['email'] = Mage::getStoreConfig('abandonedcartsconfig/email/email');
|
| 368 |
+
$sender['name'] = Mage::getStoreConfig('abandonedcartsconfig/email/name');
|
| 369 |
+
$recipient['emailTemplateVariables']['email'] = $email;
|
| 370 |
+
|
| 371 |
+
// Don't send the email if dryrun is set
|
| 372 |
+
if ($dryrun) {
|
| 373 |
+
// If the test email is set and found
|
| 374 |
+
if (isset($testemail)) {
|
| 375 |
+
// Send the email to the test email
|
| 376 |
+
Mage::getModel('core/email_template')
|
| 377 |
+
->sendTransactional(
|
| 378 |
+
$templateId,
|
| 379 |
+
$sender,
|
| 380 |
+
$testemail,
|
| 381 |
+
$recipient['emailTemplateVariables']['fullname'] ,
|
| 382 |
+
$recipient['emailTemplateVariables'],
|
| 383 |
+
null);
|
| 384 |
+
}
|
| 385 |
+
} else {
|
| 386 |
+
// Send the email
|
| 387 |
+
Mage::getModel('core/email_template')
|
| 388 |
+
->sendTransactional(
|
| 389 |
+
$templateId,
|
| 390 |
+
$sender,
|
| 391 |
+
$email,
|
| 392 |
+
$recipient['emailTemplateVariables']['fullname'] ,
|
| 393 |
+
$recipient['emailTemplateVariables'],
|
| 394 |
+
null);
|
| 395 |
+
}
|
| 396 |
+
|
| 397 |
+
if (Mage::helper('abandonedcarts')->isLogEnabled()) {
|
| 398 |
+
// Log the details
|
| 399 |
+
$comment = sprintf(
|
| 400 |
+
//"Email sent to %s, product name: %s, product image: %s, extra product count: %s, link: %s",
|
| 401 |
+
"Email sent to %s, product name: %s, product image: %s, link: %s",
|
| 402 |
+
$recipient['emailTemplateVariables']['fullname'],
|
| 403 |
+
implode(',', $recipient['emailTemplateVariables']['productname']),
|
| 404 |
+
implode(',', $recipient['emailTemplateVariables']['productimage']) ? implode(',', $recipient['emailTemplateVariables']['productimage']) : "none",
|
| 405 |
+
$recipient['emailTemplateVariables']['link']
|
| 406 |
+
);
|
| 407 |
+
|
| 408 |
+
Mage::getModel('abandonedcarts/log')->setData(
|
| 409 |
+
array(
|
| 410 |
+
'customer_email' => $email,
|
| 411 |
+
'type' => DigitalPianism_Abandonedcarts_Model_Log::TYPE_NORMAL,
|
| 412 |
+
'comment' => $comment,
|
| 413 |
+
'store' => $recipient['store_id'],
|
| 414 |
+
'dryrun' => $dryrun ? 1 : 0
|
| 415 |
+
)
|
| 416 |
+
)->save();
|
| 417 |
+
}
|
| 418 |
+
|
| 419 |
+
// Save only if dryrun is false
|
| 420 |
+
if (!$dryrun) {
|
| 421 |
+
// Load the quote
|
| 422 |
+
$quote = Mage::getModel('sales/quote')->load($recipient['cartId']);
|
| 423 |
+
|
| 424 |
+
// We change the notification attribute
|
| 425 |
+
$quote->setAbandonedNotified(1);
|
| 426 |
+
|
| 427 |
+
$quote->getResource()->saveAttribute($quote,array('abandoned_notified'));
|
| 428 |
+
}
|
| 429 |
+
}
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
/**
|
| 433 |
+
* Send notification email to customer with abandoned cart containing sale products
|
| 434 |
+
* If dryrun is set to true, it won't send emails and won't alter quotes
|
| 435 |
+
* @param boolean
|
| 436 |
+
* @param string
|
| 437 |
+
*/
|
| 438 |
+
public function sendAbandonedCartsSaleEmail($dryrun = false, $testemail = null, $emails = array())
|
| 439 |
+
{
|
| 440 |
+
if (Mage::helper('abandonedcarts')->getDryRun()) {
|
| 441 |
+
$dryrun = true;
|
| 442 |
+
}
|
| 443 |
+
|
| 444 |
+
if (Mage::helper('abandonedcarts')->getTestEmail()) {
|
| 445 |
+
$testemail = Mage::helper('abandonedcarts')->getTestEmail();
|
| 446 |
+
}
|
| 447 |
+
|
| 448 |
+
// Set customer groups
|
| 449 |
+
$this->_customerGroups = $this->_customerGroups ? $this->_customerGroups : Mage::helper('abandonedcarts')->getCustomerGroupsLimitation();
|
| 450 |
+
// Original store id
|
| 451 |
+
$this->_originalStoreId = Mage::app()->getStore()->getId();
|
| 452 |
+
try
|
| 453 |
+
{
|
| 454 |
+
if (Mage::helper('abandonedcarts')->isSaleEnabled()) {
|
| 455 |
+
|
| 456 |
+
$this->_setToday();
|
| 457 |
+
|
| 458 |
+
// Loop through the stores
|
| 459 |
+
foreach (Mage::app()->getWebsites() as $website) {
|
| 460 |
+
// Get the website id
|
| 461 |
+
$websiteId = $website->getWebsiteId();
|
| 462 |
+
foreach ($website->getGroups() as $group) {
|
| 463 |
+
$stores = $group->getStores();
|
| 464 |
+
foreach ($stores as $store) {
|
| 465 |
+
|
| 466 |
+
// Get the store id
|
| 467 |
+
$storeId = $store->getStoreId();
|
| 468 |
+
$this->_currentStoreId = $storeId;
|
| 469 |
+
|
| 470 |
+
// Init the store to be able to load the quote and the collections properly
|
| 471 |
+
Mage::app()->init($storeId,'store');
|
| 472 |
+
|
| 473 |
+
// Get the collection
|
| 474 |
+
$collection = Mage::getModel('abandonedcarts/collection')->getSalesCollection($storeId, $websiteId, $emails);
|
| 475 |
+
|
| 476 |
+
//$collection->printlogquery(true,true);
|
| 477 |
+
$collection->load();
|
| 478 |
+
|
| 479 |
+
// Skip the rest of the code if the collection is empty
|
| 480 |
+
if ($collection->getSize() == 0) {
|
| 481 |
+
continue;
|
| 482 |
+
}
|
| 483 |
+
|
| 484 |
+
// Call iterator walk method with collection query string and callback method as parameters
|
| 485 |
+
// Has to be used to handle massive collection instead of foreach
|
| 486 |
+
Mage::getSingleton('core/resource_iterator')->walk($collection->getSelect(), array(array($this, 'generateSaleRecipients')));
|
| 487 |
+
}
|
| 488 |
+
}
|
| 489 |
+
}
|
| 490 |
+
$this->_sendSaleEmails($dryrun, $testemail);
|
| 491 |
+
}
|
| 492 |
+
Mage::app()->setCurrentStore($this->_originalStoreId);
|
| 493 |
+
|
| 494 |
+
return count($this->_getSaleRecipients());
|
| 495 |
+
}
|
| 496 |
+
catch (Exception $e)
|
| 497 |
+
{
|
| 498 |
+
Mage::app()->setCurrentStore($this->_originalStoreId);
|
| 499 |
+
Mage::helper('abandonedcarts')->log(sprintf("%s->Error: %s", __METHOD__, $e->getMessage()));
|
| 500 |
+
return 0;
|
| 501 |
+
}
|
| 502 |
+
}
|
| 503 |
+
|
| 504 |
+
/**
|
| 505 |
+
* Send notification email to customer with abandoned carts after the number of days specified in the config
|
| 506 |
+
* @param bool $nodate
|
| 507 |
+
* @param bool $dryrun
|
| 508 |
+
* @param string $testemail
|
| 509 |
+
* @internal param if $boolean dryrun is set to true, it won't send emails and won't alter quotes
|
| 510 |
+
*/
|
| 511 |
+
public function sendAbandonedCartsEmail($nodate = false, $dryrun = false, $testemail = null, $emails = array())
|
| 512 |
+
{
|
| 513 |
+
if (Mage::helper('abandonedcarts')->getDryRun()) {
|
| 514 |
+
$dryrun = true;
|
| 515 |
+
}
|
| 516 |
+
|
| 517 |
+
if (Mage::helper('abandonedcarts')->getTestEmail()) {
|
| 518 |
+
$testemail = Mage::helper('abandonedcarts')->getTestEmail();
|
| 519 |
+
}
|
| 520 |
+
|
| 521 |
+
// Set customer groups
|
| 522 |
+
$this->_customerGroups = $this->_customerGroups ? $this->_customerGroups : Mage::helper('abandonedcarts')->getCustomerGroupsLimitation();
|
| 523 |
+
// Original store id
|
| 524 |
+
$this->_originalStoreId = Mage::app()->getStore()->getId();
|
| 525 |
+
try
|
| 526 |
+
{
|
| 527 |
+
if (Mage::helper('abandonedcarts')->isEnabled()) {
|
| 528 |
+
|
| 529 |
+
// Date handling
|
| 530 |
+
$store = Mage_Core_Model_App::ADMIN_STORE_ID;
|
| 531 |
+
$timezone = Mage::app()->getStore($store)->getConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_TIMEZONE);
|
| 532 |
+
date_default_timezone_set($timezone);
|
| 533 |
+
|
| 534 |
+
// If the nodate parameter is set to false
|
| 535 |
+
if (!$nodate) {
|
| 536 |
+
// Get the delay provided and convert it to a proper date
|
| 537 |
+
$delay = Mage::getStoreConfig('abandonedcartsconfig/options/notify_delay');
|
| 538 |
+
$delay = date('Y-m-d H:i:s', time() - $delay * 24 * 3600);
|
| 539 |
+
} else {
|
| 540 |
+
// We create a date in the future to handle all abandoned carts
|
| 541 |
+
$delay = date('Y-m-d H:i:s', strtotime("+7 day"));
|
| 542 |
+
}
|
| 543 |
+
|
| 544 |
+
// Loop through the stores
|
| 545 |
+
foreach (Mage::app()->getWebsites() as $website) {
|
| 546 |
+
// Get the website id
|
| 547 |
+
$websiteId = $website->getWebsiteId();
|
| 548 |
+
foreach ($website->getGroups() as $group) {
|
| 549 |
+
$stores = $group->getStores();
|
| 550 |
+
foreach ($stores as $store) {
|
| 551 |
+
|
| 552 |
+
// Get the store id
|
| 553 |
+
$storeId = $store->getStoreId();
|
| 554 |
+
$this->_currentStoreId = $storeId;
|
| 555 |
+
// Init the store to be able to load the quote and the collections properly
|
| 556 |
+
Mage::app()->init($storeId, 'store');
|
| 557 |
+
|
| 558 |
+
// Get the collection
|
| 559 |
+
$collection = Mage::getModel('abandonedcarts/collection')->getCollection($delay, $storeId, $websiteId, $emails);
|
| 560 |
+
|
| 561 |
+
//$collection->printlogquery(false,true);
|
| 562 |
+
$collection->load();
|
| 563 |
+
|
| 564 |
+
// Skip the rest of the code if the collection is empty
|
| 565 |
+
if ($collection->getSize() == 0) {
|
| 566 |
+
continue;
|
| 567 |
+
}
|
| 568 |
+
|
| 569 |
+
// Call iterator walk method with collection query string and callback method as parameters
|
| 570 |
+
// Has to be used to handle massive collection instead of foreach
|
| 571 |
+
Mage::getSingleton('core/resource_iterator')->walk($collection->getSelect(), array(array($this, 'generateRecipients')));
|
| 572 |
+
}
|
| 573 |
+
}
|
| 574 |
+
}
|
| 575 |
+
// Send the emails
|
| 576 |
+
$this->_sendEmails($dryrun, $testemail);
|
| 577 |
+
}
|
| 578 |
+
Mage::app()->setCurrentStore($this->_originalStoreId);
|
| 579 |
+
|
| 580 |
+
return count($this->_getRecipients());
|
| 581 |
+
}
|
| 582 |
+
catch (Exception $e)
|
| 583 |
+
{
|
| 584 |
+
Mage::app()->setCurrentStore($this->_originalStoreId);
|
| 585 |
+
Mage::helper('abandonedcarts')->log(sprintf("%s->Error: %s", __METHOD__, $e->getMessage()));
|
| 586 |
+
return 0;
|
| 587 |
+
}
|
| 588 |
+
}
|
| 589 |
+
|
| 590 |
+
/**
|
| 591 |
+
* @return mixed|string
|
| 592 |
+
*/
|
| 593 |
+
protected function _generateUrl($token = "")
|
| 594 |
+
{
|
| 595 |
+
if (!Mage::helper('abandonedcarts')->isCampaignEnabled()) {
|
| 596 |
+
return Mage::getUrl('abandonedcarts',
|
| 597 |
+
array(
|
| 598 |
+
'_query' => ($token ? "?token=" . $token : ''),
|
| 599 |
+
'_secure' => true
|
| 600 |
+
)
|
| 601 |
+
);
|
| 602 |
+
}
|
| 603 |
+
|
| 604 |
+
return Mage::getUrl('abandonedcarts', array(
|
| 605 |
+
'_query' => "?utm_source=" . self::CAMPAIGN_SOURCE . "&utm_medium=" . self::CAMPAIGN_MEDIUM . "&utm_campaign=" . Mage::helper('abandonedcarts')->getCampaignName() . ($token ? "&token=" . $token : ''),
|
| 606 |
+
'_secure' => true
|
| 607 |
+
)
|
| 608 |
+
);
|
| 609 |
+
}
|
| 610 |
+
|
| 611 |
+
/**
|
| 612 |
+
* @param $customerEmail
|
| 613 |
+
* @return string
|
| 614 |
+
*/
|
| 615 |
+
protected function _generateToken($customerEmail)
|
| 616 |
+
{
|
| 617 |
+
// Generate the token
|
| 618 |
+
$token = openssl_random_pseudo_bytes(9, $cstrong);
|
| 619 |
+
// Generate the token hash
|
| 620 |
+
$hash = hash("sha256", $token);
|
| 621 |
+
|
| 622 |
+
// Generate the expiration date
|
| 623 |
+
$expiration = new Zend_Date(Mage::getModel('core/date')->timestamp());
|
| 624 |
+
$expiration->addDay(self::EXPIRATION);
|
| 625 |
+
|
| 626 |
+
// Create the autologin link
|
| 627 |
+
Mage::getModel('abandonedcarts/link')->setData(
|
| 628 |
+
array(
|
| 629 |
+
'token_hash' => $hash,
|
| 630 |
+
'customer_email' => $customerEmail,
|
| 631 |
+
'expiration_date' => $expiration->toString('YYYY-MM-dd HH:mm:ss')
|
| 632 |
+
)
|
| 633 |
+
)->save();
|
| 634 |
+
|
| 635 |
+
return $token;
|
| 636 |
+
}
|
| 637 |
+
}
|
app/code/community/DigitalPianism/Abandonedcarts/Model/Observer.php
DELETED
|
@@ -1,742 +0,0 @@
|
|
| 1 |
-
<?php
|
| 2 |
-
|
| 3 |
-
/**
|
| 4 |
-
* Class DigitalPianism_Abandonedcarts_Model_Observer
|
| 5 |
-
*/
|
| 6 |
-
class DigitalPianism_Abandonedcarts_Model_Observer extends Mage_Core_Model_Abstract
|
| 7 |
-
{
|
| 8 |
-
|
| 9 |
-
protected $_recipients = array();
|
| 10 |
-
protected $_saleRecipients = array();
|
| 11 |
-
protected $_today = "";
|
| 12 |
-
protected $_customerGroups = "";
|
| 13 |
-
protected $_currentStoreId;
|
| 14 |
-
protected $_originalStoreId;
|
| 15 |
-
|
| 16 |
-
protected function _setToday()
|
| 17 |
-
{
|
| 18 |
-
// Date handling
|
| 19 |
-
$store = Mage_Core_Model_App::ADMIN_STORE_ID;
|
| 20 |
-
$timezone = Mage::app()->getStore($store)->getConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_TIMEZONE);
|
| 21 |
-
date_default_timezone_set($timezone);
|
| 22 |
-
|
| 23 |
-
// Current date
|
| 24 |
-
$currentdate = date("Ymd");
|
| 25 |
-
|
| 26 |
-
$day = (int)substr($currentdate,-2);
|
| 27 |
-
$month = (int)substr($currentdate,4,2);
|
| 28 |
-
$year = (int)substr($currentdate,0,4);
|
| 29 |
-
|
| 30 |
-
$date = array('year' => $year,'month' => $month,'day' => $day,'hour' => 23,'minute' => 59,'second' => 59);
|
| 31 |
-
|
| 32 |
-
$today = new Zend_Date($date);
|
| 33 |
-
$today->setTimeZone("UTC");
|
| 34 |
-
|
| 35 |
-
date_default_timezone_set($timezone);
|
| 36 |
-
|
| 37 |
-
$this->_today = $today->toString("Y-MM-dd HH:mm:ss");
|
| 38 |
-
}
|
| 39 |
-
|
| 40 |
-
/**
|
| 41 |
-
* @return string
|
| 42 |
-
*/
|
| 43 |
-
protected function _getToday()
|
| 44 |
-
{
|
| 45 |
-
return $this->_today;
|
| 46 |
-
}
|
| 47 |
-
|
| 48 |
-
/**
|
| 49 |
-
* @return array
|
| 50 |
-
*/
|
| 51 |
-
protected function _getRecipients()
|
| 52 |
-
{
|
| 53 |
-
return $this->_recipients;
|
| 54 |
-
}
|
| 55 |
-
|
| 56 |
-
/**
|
| 57 |
-
* @return array
|
| 58 |
-
*/
|
| 59 |
-
protected function _getSaleRecipients()
|
| 60 |
-
{
|
| 61 |
-
return $this->_saleRecipients;
|
| 62 |
-
}
|
| 63 |
-
|
| 64 |
-
/**
|
| 65 |
-
* @param $args
|
| 66 |
-
*/
|
| 67 |
-
public function generateRecipients($args)
|
| 68 |
-
{
|
| 69 |
-
// Customer group check
|
| 70 |
-
if (array_key_exists('customer_group',$args['row']) && !in_array($args['row']['customer_group'],$this->_customerGroups))
|
| 71 |
-
{
|
| 72 |
-
return;
|
| 73 |
-
}
|
| 74 |
-
|
| 75 |
-
// Test if the customer is already in the array
|
| 76 |
-
if (!array_key_exists($args['row']['customer_email'], $this->_recipients))
|
| 77 |
-
{
|
| 78 |
-
// Create an array of variables to assign to template
|
| 79 |
-
$emailTemplateVariables = array();
|
| 80 |
-
|
| 81 |
-
// Array that contains the data which will be used inside the template
|
| 82 |
-
$emailTemplateVariables['fullname'] = $args['row']['customer_firstname'].' '.$args['row']['customer_lastname'];
|
| 83 |
-
$emailTemplateVariables['firstname'] = $args['row']['customer_firstname'];
|
| 84 |
-
$emailTemplateVariables['productname'] = $args['row']['product_name'];
|
| 85 |
-
|
| 86 |
-
// Assign the values to the array of recipients
|
| 87 |
-
$this->_recipients[$args['row']['customer_email']]['cartId'] = $args['row']['cart_id'];
|
| 88 |
-
|
| 89 |
-
// Get product image via collection
|
| 90 |
-
$_productCollection = Mage::getResourceModel('catalog/product_collection');
|
| 91 |
-
// Add attributes to the collection
|
| 92 |
-
$_productCollection->addAttributeToFilter('entity_id',array('eq' => $args['row']['product_id']));
|
| 93 |
-
// Add image to the collection
|
| 94 |
-
$_productCollection->joinAttribute('image', 'catalog_product/image', 'entity_id', null, 'left');
|
| 95 |
-
// Limit the collection to get the specific product
|
| 96 |
-
$_productCollection->setPageSize(1);
|
| 97 |
-
|
| 98 |
-
try {
|
| 99 |
-
$productImg = (string)Mage::helper('catalog/image')->init($_productCollection->getFirstItem(), 'image');
|
| 100 |
-
}
|
| 101 |
-
catch (Exception $e) {
|
| 102 |
-
$productImg = false;
|
| 103 |
-
}
|
| 104 |
-
|
| 105 |
-
// Add product image
|
| 106 |
-
$emailTemplateVariables['productimage'] = $productImg;
|
| 107 |
-
|
| 108 |
-
$emailTemplateVariables['extraproductcount'] = 0;
|
| 109 |
-
}
|
| 110 |
-
else
|
| 111 |
-
{
|
| 112 |
-
// We create some extra variables if there is several products in the cart
|
| 113 |
-
$emailTemplateVariables = $this->_recipients[$args['row']['customer_email']]['emailTemplateVariables'];
|
| 114 |
-
// We increase the product count
|
| 115 |
-
$emailTemplateVariables['extraproductcount'] += 1;
|
| 116 |
-
}
|
| 117 |
-
// Assign the array of template variables
|
| 118 |
-
$this->_recipients[$args['row']['customer_email']]['emailTemplateVariables'] = $emailTemplateVariables;
|
| 119 |
-
$this->_recipients[$args['row']['customer_email']]['store_id'] = $this->_currentStoreId;
|
| 120 |
-
}
|
| 121 |
-
|
| 122 |
-
/**
|
| 123 |
-
* @param $args
|
| 124 |
-
*/
|
| 125 |
-
public function generateSaleRecipients($args)
|
| 126 |
-
{
|
| 127 |
-
// Customer group check
|
| 128 |
-
if (array_key_exists('customer_group',$args['row']) && !in_array($args['row']['customer_group'],$this->_customerGroups))
|
| 129 |
-
{
|
| 130 |
-
return;
|
| 131 |
-
}
|
| 132 |
-
|
| 133 |
-
// Double check if the special from date is set
|
| 134 |
-
if (!array_key_exists('product_special_from_date',$args['row']) || !$args['row']['product_special_from_date'])
|
| 135 |
-
{
|
| 136 |
-
// If not we use today for the comparison
|
| 137 |
-
$fromDate = $this->_getToday();
|
| 138 |
-
}
|
| 139 |
-
else $fromDate = $args['row']['product_special_from_date'];
|
| 140 |
-
|
| 141 |
-
// Do the same for the special to date
|
| 142 |
-
if (!array_key_exists('product_special_to_date',$args['row']) || !$args['row']['product_special_to_date'])
|
| 143 |
-
{
|
| 144 |
-
$toDate = $this->_getToday();
|
| 145 |
-
}
|
| 146 |
-
else $toDate = $args['row']['product_special_to_date'];
|
| 147 |
-
|
| 148 |
-
// We need to ensure that the price in cart is higher than the new special price
|
| 149 |
-
// As well as the date comparison in case the sale is over or hasn't started
|
| 150 |
-
if ($args['row']['product_price_in_cart'] > 0.00
|
| 151 |
-
&& $args['row']['product_special_price'] > 0.00
|
| 152 |
-
&& ($args['row']['product_price_in_cart'] > $args['row']['product_special_price'])
|
| 153 |
-
&& ($fromDate <= $this->_getToday())
|
| 154 |
-
&& ($toDate >= $this->_getToday()))
|
| 155 |
-
{
|
| 156 |
-
|
| 157 |
-
// Test if the customer is already in the array
|
| 158 |
-
if (!array_key_exists($args['row']['customer_email'], $this->_saleRecipients))
|
| 159 |
-
{
|
| 160 |
-
// Create an array of variables to assign to template
|
| 161 |
-
$emailTemplateVariables = array();
|
| 162 |
-
|
| 163 |
-
// Array that contains the data which will be used inside the template
|
| 164 |
-
$emailTemplateVariables['fullname'] = $args['row']['customer_firstname'].' '.$args['row']['customer_lastname'];
|
| 165 |
-
$emailTemplateVariables['firstname'] = $args['row']['customer_firstname'];
|
| 166 |
-
$emailTemplateVariables['productname'] = $args['row']['product_name'];
|
| 167 |
-
$emailTemplateVariables['cartprice'] = number_format($args['row']['product_price_in_cart'],2);
|
| 168 |
-
$emailTemplateVariables['specialprice'] = number_format($args['row']['product_special_price'],2);
|
| 169 |
-
|
| 170 |
-
// Assign the values to the array of recipients
|
| 171 |
-
$this->_saleRecipients[$args['row']['customer_email']]['cartId'] = $args['row']['cart_id'];
|
| 172 |
-
|
| 173 |
-
// Get product image via collection
|
| 174 |
-
$_productCollection = Mage::getResourceModel('catalog/product_collection');
|
| 175 |
-
// Add attributes to the collection
|
| 176 |
-
$_productCollection->addAttributeToFilter('entity_id',array('eq' => $args['row']['product_id']));
|
| 177 |
-
// Add image to the collection
|
| 178 |
-
$_productCollection->joinAttribute('image', 'catalog_product/image', 'entity_id', null, 'left');
|
| 179 |
-
// Limit the collection to get the specific product
|
| 180 |
-
$_productCollection->setPageSize(1);
|
| 181 |
-
|
| 182 |
-
try {
|
| 183 |
-
$productImg = (string)Mage::helper('catalog/image')->init($_productCollection->getFirstItem(), 'image');
|
| 184 |
-
}
|
| 185 |
-
catch (Exception $e) {
|
| 186 |
-
$productImg = false;
|
| 187 |
-
}
|
| 188 |
-
|
| 189 |
-
// Add product image
|
| 190 |
-
$emailTemplateVariables['productimage'] = $productImg;
|
| 191 |
-
}
|
| 192 |
-
else
|
| 193 |
-
{
|
| 194 |
-
// We create some extra variables if there is several products in the cart
|
| 195 |
-
$emailTemplateVariables = $this->_saleRecipients[$args['row']['customer_email']]['emailTemplateVariables'];
|
| 196 |
-
// Discount amount
|
| 197 |
-
// If one product before
|
| 198 |
-
if (!array_key_exists('discount',$emailTemplateVariables))
|
| 199 |
-
{
|
| 200 |
-
$emailTemplateVariables['discount'] = $emailTemplateVariables['cartprice'] - $emailTemplateVariables['specialprice'];
|
| 201 |
-
}
|
| 202 |
-
// We add the discount on the second product
|
| 203 |
-
$moreDiscount = number_format($args['row']['product_price_in_cart'],2) - number_format($args['row']['product_special_price'],2);
|
| 204 |
-
$emailTemplateVariables['discount'] += $moreDiscount;
|
| 205 |
-
// We increase the product count
|
| 206 |
-
if (!array_key_exists('extraproductcount',$emailTemplateVariables))
|
| 207 |
-
{
|
| 208 |
-
$emailTemplateVariables['extraproductcount'] = 0;
|
| 209 |
-
}
|
| 210 |
-
$emailTemplateVariables['extraproductcount'] += 1;
|
| 211 |
-
}
|
| 212 |
-
|
| 213 |
-
// Add currency codes to prices
|
| 214 |
-
$emailTemplateVariables['cartprice'] = Mage::helper('core')->currency($emailTemplateVariables['cartprice'], true, false);
|
| 215 |
-
$emailTemplateVariables['specialprice'] = Mage::helper('core')->currency($emailTemplateVariables['specialprice'], true, false);
|
| 216 |
-
if (array_key_exists('discount',$emailTemplateVariables))
|
| 217 |
-
{
|
| 218 |
-
$emailTemplateVariables['discount'] = Mage::helper('core')->currency($emailTemplateVariables['discount'], true, false);
|
| 219 |
-
}
|
| 220 |
-
|
| 221 |
-
// Assign the array of template variables
|
| 222 |
-
$this->_saleRecipients[$args['row']['customer_email']]['emailTemplateVariables'] = $emailTemplateVariables;
|
| 223 |
-
$this->_saleRecipients[$args['row']['customer_email']]['store_id'] = $this->_currentStoreId;
|
| 224 |
-
}
|
| 225 |
-
}
|
| 226 |
-
|
| 227 |
-
/**
|
| 228 |
-
* @param $dryrun
|
| 229 |
-
* @param $testemail
|
| 230 |
-
*/
|
| 231 |
-
protected function _sendSaleEmails($dryrun,$testemail)
|
| 232 |
-
{
|
| 233 |
-
try
|
| 234 |
-
{
|
| 235 |
-
// Send the emails via a loop
|
| 236 |
-
foreach ($this->_getSaleRecipients() as $email => $recipient)
|
| 237 |
-
{
|
| 238 |
-
// Store Id
|
| 239 |
-
Mage::app()->setCurrentStore($recipient['store_id']);
|
| 240 |
-
// Get the transactional email template
|
| 241 |
-
$templateId = Mage::getStoreConfig('abandonedcartsconfig/options/email_template_sale');
|
| 242 |
-
// Get the sender
|
| 243 |
-
$sender = array();
|
| 244 |
-
$sender['email'] = Mage::getStoreConfig('abandonedcartsconfig/options/email');
|
| 245 |
-
$sender['name'] = Mage::getStoreConfig('abandonedcartsconfig/options/name');
|
| 246 |
-
|
| 247 |
-
// Don't send the email if dryrun is set
|
| 248 |
-
if ($dryrun)
|
| 249 |
-
{
|
| 250 |
-
// Log data when dried run
|
| 251 |
-
Mage::helper('abandonedcarts')->log(__METHOD__);
|
| 252 |
-
Mage::helper('abandonedcarts')->log($recipient['emailTemplateVariables']);
|
| 253 |
-
// If the test email is set and found
|
| 254 |
-
if (isset($testemail) && $email == $testemail)
|
| 255 |
-
{
|
| 256 |
-
Mage::helper('abandonedcarts')->log(__METHOD__ . "sendAbandonedCartsSaleEmail test: " . $email);
|
| 257 |
-
// Send the test email
|
| 258 |
-
Mage::getModel('core/email_template')
|
| 259 |
-
->sendTransactional(
|
| 260 |
-
$templateId,
|
| 261 |
-
$sender,
|
| 262 |
-
$email,
|
| 263 |
-
$recipient['emailTemplateVariables']['fullname'] ,
|
| 264 |
-
$recipient['emailTemplateVariables'],
|
| 265 |
-
null);
|
| 266 |
-
}
|
| 267 |
-
}
|
| 268 |
-
else
|
| 269 |
-
{
|
| 270 |
-
Mage::helper('abandonedcarts')->log(__METHOD__ . "sendAbandonedCartsSaleEmail: " . $email);
|
| 271 |
-
|
| 272 |
-
// Send the email
|
| 273 |
-
Mage::getModel('core/email_template')
|
| 274 |
-
->sendTransactional(
|
| 275 |
-
$templateId,
|
| 276 |
-
$sender,
|
| 277 |
-
$email,
|
| 278 |
-
$recipient['emailTemplateVariables']['fullname'] ,
|
| 279 |
-
$recipient['emailTemplateVariables'],
|
| 280 |
-
null);
|
| 281 |
-
}
|
| 282 |
-
|
| 283 |
-
// Load the quote
|
| 284 |
-
$quote = Mage::getModel('sales/quote')->load($recipient['cartId']);
|
| 285 |
-
|
| 286 |
-
// We change the notification attribute
|
| 287 |
-
$quote->setAbandonedSaleNotified(1);
|
| 288 |
-
|
| 289 |
-
// Save only if dryrun is false or if the test email is set and found
|
| 290 |
-
if (!$dryrun || (isset($testemail) && $email == $testemail))
|
| 291 |
-
{
|
| 292 |
-
$quote->getResource()->saveAttribute($quote,array('abandoned_sale_notified'));
|
| 293 |
-
}
|
| 294 |
-
}
|
| 295 |
-
}
|
| 296 |
-
catch (Exception $e)
|
| 297 |
-
{
|
| 298 |
-
Mage::helper('abandonedcarts')->log(__METHOD__ . " " . $e->getMessage());
|
| 299 |
-
}
|
| 300 |
-
}
|
| 301 |
-
|
| 302 |
-
/**
|
| 303 |
-
* @param $dryrun
|
| 304 |
-
* @param $testemail
|
| 305 |
-
*/
|
| 306 |
-
protected function _sendEmails($dryrun,$testemail)
|
| 307 |
-
{
|
| 308 |
-
try
|
| 309 |
-
{
|
| 310 |
-
// Send the emails via a loop
|
| 311 |
-
foreach ($this->_getRecipients() as $email => $recipient)
|
| 312 |
-
{
|
| 313 |
-
// Store Id
|
| 314 |
-
Mage::app()->setCurrentStore($recipient['store_id']);
|
| 315 |
-
// Get the transactional email template
|
| 316 |
-
$templateId = Mage::getStoreConfig('abandonedcartsconfig/options/email_template');
|
| 317 |
-
// Get the sender
|
| 318 |
-
$sender = array();
|
| 319 |
-
$sender['email'] = Mage::getStoreConfig('abandonedcartsconfig/options/email');
|
| 320 |
-
$sender['name'] = Mage::getStoreConfig('abandonedcartsconfig/options/name');
|
| 321 |
-
|
| 322 |
-
// Don't send the email if dryrun is set
|
| 323 |
-
if ($dryrun)
|
| 324 |
-
{
|
| 325 |
-
// Log data when dried run
|
| 326 |
-
Mage::helper('abandonedcarts')->log(__METHOD__);
|
| 327 |
-
Mage::helper('abandonedcarts')->log($recipient['emailTemplateVariables']);
|
| 328 |
-
// If the test email is set and found
|
| 329 |
-
if (isset($testemail) && $email == $testemail)
|
| 330 |
-
{
|
| 331 |
-
Mage::helper('abandonedcarts')->log(__METHOD__ . "sendAbandonedCartsEmail test: " . $email);
|
| 332 |
-
// Send the test email
|
| 333 |
-
Mage::getModel('core/email_template')
|
| 334 |
-
->sendTransactional(
|
| 335 |
-
$templateId,
|
| 336 |
-
$sender,
|
| 337 |
-
$email,
|
| 338 |
-
$recipient['emailTemplateVariables']['fullname'] ,
|
| 339 |
-
$recipient['emailTemplateVariables'],
|
| 340 |
-
null);
|
| 341 |
-
}
|
| 342 |
-
}
|
| 343 |
-
else
|
| 344 |
-
{
|
| 345 |
-
Mage::helper('abandonedcarts')->log(__METHOD__ . "sendAbandonedCartsEmail: " . $email);
|
| 346 |
-
|
| 347 |
-
// Send the email
|
| 348 |
-
Mage::getModel('core/email_template')
|
| 349 |
-
->sendTransactional(
|
| 350 |
-
$templateId,
|
| 351 |
-
$sender,
|
| 352 |
-
$email,
|
| 353 |
-
$recipient['emailTemplateVariables']['fullname'] ,
|
| 354 |
-
$recipient['emailTemplateVariables'],
|
| 355 |
-
null);
|
| 356 |
-
}
|
| 357 |
-
|
| 358 |
-
// Load the quote
|
| 359 |
-
$quote = Mage::getModel('sales/quote')->load($recipient['cartId']);
|
| 360 |
-
|
| 361 |
-
// We change the notification attribute
|
| 362 |
-
$quote->setAbandonedNotified(1);
|
| 363 |
-
|
| 364 |
-
// Save only if dryrun is false or if the test email is set and found
|
| 365 |
-
if (!$dryrun || (isset($testemail) && $email == $testemail))
|
| 366 |
-
{
|
| 367 |
-
$quote->getResource()->saveAttribute($quote,array('abandoned_notified'));
|
| 368 |
-
}
|
| 369 |
-
}
|
| 370 |
-
}
|
| 371 |
-
catch (Exception $e)
|
| 372 |
-
{
|
| 373 |
-
Mage::helper('abandonedcarts')->log(__METHOD__ . " " . $e->getMessage());
|
| 374 |
-
}
|
| 375 |
-
}
|
| 376 |
-
|
| 377 |
-
/**
|
| 378 |
-
* Send notification email to customer with abandoned cart containing sale products
|
| 379 |
-
* @param boolean $dryrun if dryrun is set to true, it won't send emails and won't alter quotes
|
| 380 |
-
* @param string $testemail email to test
|
| 381 |
-
*/
|
| 382 |
-
public function sendAbandonedCartsSaleEmail($dryrun = false, $testemail = null)
|
| 383 |
-
{
|
| 384 |
-
try
|
| 385 |
-
{
|
| 386 |
-
if (Mage::helper('abandonedcarts')->getDryRun()) $dryrun = true;
|
| 387 |
-
if (Mage::helper('abandonedcarts')->getTestEmail()) $testemail = Mage::helper('abandonedcarts')->getTestEmail();
|
| 388 |
-
// Set customer groups
|
| 389 |
-
$this->_customerGroups = $this->_customerGroups ? $this->_customerGroups : Mage::helper('abandonedcarts')->getCustomerGroupsLimitation();
|
| 390 |
-
// Original store id
|
| 391 |
-
$this->_originalStoreId = Mage::app()->getStore()->getId();
|
| 392 |
-
|
| 393 |
-
if (Mage::helper('abandonedcarts')->isSaleEnabled())
|
| 394 |
-
{
|
| 395 |
-
$this->_setToday();
|
| 396 |
-
|
| 397 |
-
// Get the attribute id for the status attribute
|
| 398 |
-
$eavAttribute = Mage::getModel('eav/entity_attribute');
|
| 399 |
-
$statusId = $eavAttribute->getIdByCode('catalog_product', 'status');
|
| 400 |
-
$nameId = $eavAttribute->getIdByCode('catalog_product', 'name');
|
| 401 |
-
$priceId = $eavAttribute->getIdByCode('catalog_product', 'price');
|
| 402 |
-
$spriceId = $eavAttribute->getIdByCode('catalog_product', 'special_price');
|
| 403 |
-
$spfromId = $eavAttribute->getIdByCode('catalog_product', 'special_from_date');
|
| 404 |
-
$sptoId = $eavAttribute->getIdByCode('catalog_product', 'special_to_date');
|
| 405 |
-
|
| 406 |
-
// Loop through the stores
|
| 407 |
-
foreach (Mage::app()->getWebsites() as $website) {
|
| 408 |
-
// Get the website id
|
| 409 |
-
$websiteId = $website->getWebsiteId();
|
| 410 |
-
foreach ($website->getGroups() as $group) {
|
| 411 |
-
$stores = $group->getStores();
|
| 412 |
-
foreach ($stores as $store) {
|
| 413 |
-
|
| 414 |
-
// Get the store id
|
| 415 |
-
$storeId = $store->getStoreId();
|
| 416 |
-
$this->_currentStoreId = $storeId;
|
| 417 |
-
|
| 418 |
-
// Init the store to be able to load the quote and the collections properly
|
| 419 |
-
Mage::app()->init($storeId,'store');
|
| 420 |
-
|
| 421 |
-
// Get the product collection
|
| 422 |
-
$collection = Mage::getResourceModel('catalog/product_collection')->setStore($storeId);
|
| 423 |
-
|
| 424 |
-
// Database TableNams
|
| 425 |
-
$eavEntityType = Mage::getSingleton("core/resource")->getTableName('eav_entity_type');
|
| 426 |
-
$eavAttribute = Mage::getSingleton("core/resource")->getTableName('eav_attribute');
|
| 427 |
-
|
| 428 |
-
// If flat catalog is enabled
|
| 429 |
-
if (Mage::helper('catalog/product_flat')->isEnabled())
|
| 430 |
-
{
|
| 431 |
-
// First collection: carts with products that became on sale
|
| 432 |
-
// Join the collection with the required tables
|
| 433 |
-
$collection->getSelect()
|
| 434 |
-
->reset(Zend_Db_Select::COLUMNS)
|
| 435 |
-
->columns(array('e.entity_id AS product_id',
|
| 436 |
-
'e.sku',
|
| 437 |
-
'catalog_flat.name as product_name',
|
| 438 |
-
'catalog_flat.price as product_price',
|
| 439 |
-
'catalog_flat.special_price as product_special_price',
|
| 440 |
-
'catalog_flat.special_from_date as product_special_from_date',
|
| 441 |
-
'catalog_flat.special_to_date as product_special_to_date',
|
| 442 |
-
'quote_table.entity_id as cart_id',
|
| 443 |
-
'quote_table.updated_at as cart_updated_at',
|
| 444 |
-
'quote_table.abandoned_sale_notified as has_been_notified',
|
| 445 |
-
'quote_items.price as product_price_in_cart',
|
| 446 |
-
'quote_table.customer_email as customer_email',
|
| 447 |
-
'quote_table.customer_firstname as customer_firstname',
|
| 448 |
-
'quote_table.customer_lastname as customer_lastname',
|
| 449 |
-
'quote_table.customer_group_id as customer_group'
|
| 450 |
-
)
|
| 451 |
-
)
|
| 452 |
-
->joinInner(
|
| 453 |
-
array('quote_items' => Mage::getSingleton("core/resource")->getTableName('sales_flat_quote_item')),
|
| 454 |
-
'quote_items.product_id = e.entity_id AND quote_items.price > 0.00',
|
| 455 |
-
null)
|
| 456 |
-
->joinInner(
|
| 457 |
-
array('quote_table' => Mage::getSingleton("core/resource")->getTableName('sales_flat_quote')),
|
| 458 |
-
'quote_items.quote_id = quote_table.entity_id AND quote_table.items_count > 0 AND quote_table.is_active = 1 AND quote_table.customer_email IS NOT NULL AND quote_table.abandoned_sale_notified = 0 AND quote_table.store_id = '.$storeId,
|
| 459 |
-
null)
|
| 460 |
-
->joinInner(
|
| 461 |
-
array('catalog_flat' => Mage::getSingleton("core/resource")->getTableName('catalog_product_flat_'.$storeId)),
|
| 462 |
-
'catalog_flat.entity_id = e.entity_id',
|
| 463 |
-
null)
|
| 464 |
-
->joinInner(
|
| 465 |
-
array('catalog_enabled' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_int')),
|
| 466 |
-
'catalog_enabled.entity_id = e.entity_id AND catalog_enabled.attribute_id = '.$statusId.' AND catalog_enabled.value = 1',
|
| 467 |
-
null)
|
| 468 |
-
->joinInner(
|
| 469 |
-
array('inventory' => Mage::getSingleton("core/resource")->getTableName('cataloginventory_stock_status')),
|
| 470 |
-
'inventory.product_id = e.entity_id AND inventory.stock_status = 1 AND inventory.website_id = '.$websiteId,
|
| 471 |
-
null)
|
| 472 |
-
->order('quote_table.updated_at DESC');
|
| 473 |
-
}
|
| 474 |
-
else
|
| 475 |
-
{
|
| 476 |
-
// First collection: carts with products that became on sale
|
| 477 |
-
// Join the collection with the required tables
|
| 478 |
-
$collection->getSelect()
|
| 479 |
-
->reset(Zend_Db_Select::COLUMNS)
|
| 480 |
-
->columns(array('e.entity_id AS product_id',
|
| 481 |
-
'e.sku',
|
| 482 |
-
'catalog_name.value as product_name',
|
| 483 |
-
'catalog_price.value as product_price',
|
| 484 |
-
'catalog_sprice.value as product_special_price',
|
| 485 |
-
'catalog_spfrom.value as product_special_from_date',
|
| 486 |
-
'catalog_spto.value as product_special_to_date',
|
| 487 |
-
'quote_table.entity_id as cart_id',
|
| 488 |
-
'quote_table.updated_at as cart_updated_at',
|
| 489 |
-
'quote_table.abandoned_sale_notified as has_been_notified',
|
| 490 |
-
'quote_items.price as product_price_in_cart',
|
| 491 |
-
'quote_table.customer_email as customer_email',
|
| 492 |
-
'quote_table.customer_firstname as customer_firstname',
|
| 493 |
-
'quote_table.customer_lastname as customer_lastname',
|
| 494 |
-
'quote_table.customer_group_id as customer_group'
|
| 495 |
-
)
|
| 496 |
-
)
|
| 497 |
-
// Name
|
| 498 |
-
->joinInner(
|
| 499 |
-
array('catalog_name' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_varchar')),
|
| 500 |
-
"catalog_name.entity_id = e.entity_id AND catalog_name.attribute_id = $nameId",
|
| 501 |
-
null)
|
| 502 |
-
// Price
|
| 503 |
-
->joinInner(
|
| 504 |
-
array('catalog_price' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_decimal')),
|
| 505 |
-
"catalog_price.entity_id = e.entity_id AND catalog_price.attribute_id = $priceId",
|
| 506 |
-
null)
|
| 507 |
-
// Special Price
|
| 508 |
-
->joinInner(
|
| 509 |
-
array('catalog_sprice' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_decimal')),
|
| 510 |
-
"catalog_sprice.entity_id = e.entity_id AND catalog_sprice.attribute_id = $spriceId",
|
| 511 |
-
null)
|
| 512 |
-
// Special From Date
|
| 513 |
-
->joinInner(
|
| 514 |
-
array('catalog_spfrom' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_datetime')),
|
| 515 |
-
"catalog_spfrom.entity_id = e.entity_id AND catalog_spfrom.attribute_id = $spfromId",
|
| 516 |
-
null)
|
| 517 |
-
// Special To Date
|
| 518 |
-
->joinInner(
|
| 519 |
-
array('catalog_spto' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_datetime')),
|
| 520 |
-
"catalog_spto.entity_id = e.entity_id AND catalog_spto.attribute_id = $sptoId",
|
| 521 |
-
null)
|
| 522 |
-
->joinInner(
|
| 523 |
-
array('quote_items' => Mage::getSingleton("core/resource")->getTableName('sales_flat_quote_item')),
|
| 524 |
-
'quote_items.product_id = e.entity_id AND quote_items.price > 0.00',
|
| 525 |
-
null)
|
| 526 |
-
->joinInner(
|
| 527 |
-
array('quote_table' => Mage::getSingleton("core/resource")->getTableName('sales_flat_quote')),
|
| 528 |
-
'quote_items.quote_id = quote_table.entity_id AND quote_table.items_count > 0 AND quote_table.is_active = 1 AND quote_table.customer_email IS NOT NULL AND quote_table.abandoned_sale_notified = 0 AND quote_table.store_id = '.$storeId,
|
| 529 |
-
null)
|
| 530 |
-
->joinInner(
|
| 531 |
-
array('catalog_enabled' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_int')),
|
| 532 |
-
'catalog_enabled.entity_id = e.entity_id AND catalog_enabled.attribute_id = '.$statusId.' AND catalog_enabled.value = 1',
|
| 533 |
-
null)
|
| 534 |
-
->joinInner(
|
| 535 |
-
array('inventory' => Mage::getSingleton("core/resource")->getTableName('cataloginventory_stock_status')),
|
| 536 |
-
'inventory.product_id = e.entity_id AND inventory.stock_status = 1 AND inventory.website_id = '.$websiteId,
|
| 537 |
-
null)
|
| 538 |
-
->order('quote_table.updated_at DESC');
|
| 539 |
-
}
|
| 540 |
-
|
| 541 |
-
//$collection->printlogquery(true,true);
|
| 542 |
-
$collection->load();
|
| 543 |
-
|
| 544 |
-
// Skip the rest of the code if the collection is empty
|
| 545 |
-
if ($collection->getSize() == 0) continue;
|
| 546 |
-
|
| 547 |
-
// Call iterator walk method with collection query string and callback method as parameters
|
| 548 |
-
// Has to be used to handle massive collection instead of foreach
|
| 549 |
-
Mage::getSingleton('core/resource_iterator')->walk($collection->getSelect(), array(array($this, 'generateSaleRecipients')));
|
| 550 |
-
}
|
| 551 |
-
}
|
| 552 |
-
}
|
| 553 |
-
|
| 554 |
-
// Send the emails
|
| 555 |
-
$this->_sendSaleEmails($dryrun,$testemail);
|
| 556 |
-
}
|
| 557 |
-
|
| 558 |
-
Mage::app()->setCurrentStore($this->_originalStoreId);
|
| 559 |
-
}
|
| 560 |
-
catch (Exception $e)
|
| 561 |
-
{
|
| 562 |
-
Mage::app()->setCurrentStore($this->_originalStoreId);
|
| 563 |
-
Mage::helper('abandonedcarts')->log(__METHOD__ . " " . $e->getMessage());
|
| 564 |
-
}
|
| 565 |
-
}
|
| 566 |
-
|
| 567 |
-
/**
|
| 568 |
-
* Send notification email to customer with abandoned carts after the number of days specified in the config
|
| 569 |
-
* @param bool $nodate
|
| 570 |
-
* @param boolean $dryrun if dryrun is set to true, it won't send emails and won't alter quotes
|
| 571 |
-
* @param string $testemail email to test
|
| 572 |
-
*/
|
| 573 |
-
public function sendAbandonedCartsEmail($nodate = false, $dryrun = false, $testemail = null)
|
| 574 |
-
{
|
| 575 |
-
if (Mage::helper('abandonedcarts')->getDryRun()) $dryrun = true;
|
| 576 |
-
if (Mage::helper('abandonedcarts')->getTestEmail()) $testemail = Mage::helper('abandonedcarts')->getTestEmail();
|
| 577 |
-
// Set customer groups
|
| 578 |
-
$this->_customerGroups = $this->_customerGroups ? $this->_customerGroups : Mage::helper('abandonedcarts')->getCustomerGroupsLimitation();
|
| 579 |
-
|
| 580 |
-
$this->_originalStoreId = Mage::app()->getStore()->getId();
|
| 581 |
-
|
| 582 |
-
try
|
| 583 |
-
{
|
| 584 |
-
if (Mage::helper('abandonedcarts')->isEnabled())
|
| 585 |
-
{
|
| 586 |
-
// Date handling
|
| 587 |
-
$store = Mage_Core_Model_App::ADMIN_STORE_ID;
|
| 588 |
-
$timezone = Mage::app()->getStore($store)->getConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_TIMEZONE);
|
| 589 |
-
date_default_timezone_set($timezone);
|
| 590 |
-
|
| 591 |
-
// If the nodate parameter is set to false
|
| 592 |
-
if (!$nodate)
|
| 593 |
-
{
|
| 594 |
-
// Get the delay provided and convert it to a proper date
|
| 595 |
-
$delay = Mage::getStoreConfig('abandonedcartsconfig/options/notify_delay');
|
| 596 |
-
$delay = date('Y-m-d H:i:s', time() - $delay * 24 * 3600);
|
| 597 |
-
}
|
| 598 |
-
else
|
| 599 |
-
{
|
| 600 |
-
// We create a date in the future to handle all abandoned carts
|
| 601 |
-
$delay = date('Y-m-d H:i:s', strtotime("+7 day"));
|
| 602 |
-
}
|
| 603 |
-
|
| 604 |
-
// Get the attribute id for several attributes
|
| 605 |
-
$eavAttribute = Mage::getModel('eav/entity_attribute');
|
| 606 |
-
$statusId = $eavAttribute->getIdByCode('catalog_product', 'status');
|
| 607 |
-
$nameId = $eavAttribute->getIdByCode('catalog_product', 'name');
|
| 608 |
-
$priceId = $eavAttribute->getIdByCode('catalog_product', 'price');
|
| 609 |
-
|
| 610 |
-
// Loop through the stores
|
| 611 |
-
foreach (Mage::app()->getWebsites() as $website) {
|
| 612 |
-
// Get the website id
|
| 613 |
-
$websiteId = $website->getWebsiteId();
|
| 614 |
-
foreach ($website->getGroups() as $group) {
|
| 615 |
-
$stores = $group->getStores();
|
| 616 |
-
foreach ($stores as $store) {
|
| 617 |
-
|
| 618 |
-
// Get the store id
|
| 619 |
-
$storeId = $store->getStoreId();
|
| 620 |
-
$this->_currentStoreId = $storeId;
|
| 621 |
-
// Init the store to be able to load the quote and the collections properly
|
| 622 |
-
Mage::app()->init($storeId,'store');
|
| 623 |
-
|
| 624 |
-
// Get the product collection
|
| 625 |
-
$collection = Mage::getResourceModel('catalog/product_collection')->setStore($storeId);
|
| 626 |
-
|
| 627 |
-
// If flat catalog is enabled
|
| 628 |
-
if (Mage::helper('catalog/product_flat')->isEnabled())
|
| 629 |
-
{
|
| 630 |
-
// First collection: carts with products that became on sale
|
| 631 |
-
// Join the collection with the required tables
|
| 632 |
-
$collection->getSelect()
|
| 633 |
-
->reset(Zend_Db_Select::COLUMNS)
|
| 634 |
-
->columns(array('e.entity_id AS product_id',
|
| 635 |
-
'e.sku',
|
| 636 |
-
'catalog_flat.name as product_name',
|
| 637 |
-
'catalog_flat.price as product_price',
|
| 638 |
-
'quote_table.entity_id as cart_id',
|
| 639 |
-
'quote_table.updated_at as cart_updated_at',
|
| 640 |
-
'quote_table.abandoned_notified as has_been_notified',
|
| 641 |
-
'quote_table.customer_email as customer_email',
|
| 642 |
-
'quote_table.customer_firstname as customer_firstname',
|
| 643 |
-
'quote_table.customer_lastname as customer_lastname',
|
| 644 |
-
'quote_table.customer_group_id as customer_group'
|
| 645 |
-
)
|
| 646 |
-
)
|
| 647 |
-
->joinInner(
|
| 648 |
-
array('quote_items' => Mage::getSingleton("core/resource")->getTableName('sales_flat_quote_item')),
|
| 649 |
-
'quote_items.product_id = e.entity_id AND quote_items.price > 0.00',
|
| 650 |
-
null)
|
| 651 |
-
->joinInner(
|
| 652 |
-
array('quote_table' => Mage::getSingleton("core/resource")->getTableName('sales_flat_quote')),
|
| 653 |
-
'quote_items.quote_id = quote_table.entity_id AND quote_table.items_count > 0 AND quote_table.is_active = 1 AND quote_table.customer_email IS NOT NULL AND quote_table.abandoned_notified = 0 AND quote_table.updated_at < "'.$delay.'" AND quote_table.store_id = '.$storeId,
|
| 654 |
-
null)
|
| 655 |
-
->joinInner(
|
| 656 |
-
array('catalog_flat' => Mage::getSingleton("core/resource")->getTableName('catalog_product_flat_'.$storeId)),
|
| 657 |
-
'catalog_flat.entity_id = e.entity_id',
|
| 658 |
-
null)
|
| 659 |
-
->joinInner(
|
| 660 |
-
array('catalog_enabled' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_int')),
|
| 661 |
-
'catalog_enabled.entity_id = e.entity_id AND catalog_enabled.attribute_id = '.$statusId.' AND catalog_enabled.value = 1',
|
| 662 |
-
null)
|
| 663 |
-
->joinInner(
|
| 664 |
-
array('inventory' => Mage::getSingleton("core/resource")->getTableName('cataloginventory_stock_status')),
|
| 665 |
-
'inventory.product_id = e.entity_id AND inventory.stock_status = 1 AND website_id = '.$websiteId,
|
| 666 |
-
null)
|
| 667 |
-
->order('quote_table.updated_at DESC');
|
| 668 |
-
}
|
| 669 |
-
else
|
| 670 |
-
{
|
| 671 |
-
// First collection: carts with products that became on sale
|
| 672 |
-
// Join the collection with the required tables
|
| 673 |
-
$collection->getSelect()
|
| 674 |
-
->reset(Zend_Db_Select::COLUMNS)
|
| 675 |
-
->columns(array('e.entity_id AS product_id',
|
| 676 |
-
'e.sku',
|
| 677 |
-
'catalog_name.value as product_name',
|
| 678 |
-
'catalog_price.value as product_price',
|
| 679 |
-
'quote_table.entity_id as cart_id',
|
| 680 |
-
'quote_table.updated_at as cart_updated_at',
|
| 681 |
-
'quote_table.abandoned_notified as has_been_notified',
|
| 682 |
-
'quote_table.customer_email as customer_email',
|
| 683 |
-
'quote_table.customer_firstname as customer_firstname',
|
| 684 |
-
'quote_table.customer_lastname as customer_lastname',
|
| 685 |
-
'quote_table.customer_group_id as customer_group'
|
| 686 |
-
)
|
| 687 |
-
)
|
| 688 |
-
// Name
|
| 689 |
-
->joinInner(
|
| 690 |
-
array('catalog_name' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_varchar')),
|
| 691 |
-
"catalog_name.entity_id = e.entity_id AND catalog_name.attribute_id = $nameId",
|
| 692 |
-
null)
|
| 693 |
-
// Price
|
| 694 |
-
->joinInner(
|
| 695 |
-
array('catalog_price' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_decimal')),
|
| 696 |
-
"catalog_price.entity_id = e.entity_id AND catalog_price.attribute_id = $priceId",
|
| 697 |
-
null)
|
| 698 |
-
->joinInner(
|
| 699 |
-
array('quote_items' => Mage::getSingleton("core/resource")->getTableName('sales_flat_quote_item')),
|
| 700 |
-
'quote_items.product_id = e.entity_id AND quote_items.price > 0.00',
|
| 701 |
-
null)
|
| 702 |
-
->joinInner(
|
| 703 |
-
array('quote_table' => Mage::getSingleton("core/resource")->getTableName('sales_flat_quote')),
|
| 704 |
-
'quote_items.quote_id = quote_table.entity_id AND quote_table.items_count > 0 AND quote_table.is_active = 1 AND quote_table.customer_email IS NOT NULL AND quote_table.abandoned_notified = 0 AND quote_table.updated_at < "'.$delay.'" AND quote_table.store_id = '.$storeId,
|
| 705 |
-
null)
|
| 706 |
-
->joinInner(
|
| 707 |
-
array('catalog_enabled' => Mage::getSingleton("core/resource")->getTableName('catalog_product_entity_int')),
|
| 708 |
-
'catalog_enabled.entity_id = e.entity_id AND catalog_enabled.attribute_id = '.$statusId.' AND catalog_enabled.value = 1',
|
| 709 |
-
null)
|
| 710 |
-
->joinInner(
|
| 711 |
-
array('inventory' => Mage::getSingleton("core/resource")->getTableName('cataloginventory_stock_status')),
|
| 712 |
-
'inventory.product_id = e.entity_id AND inventory.stock_status = 1 AND website_id = '.$websiteId,
|
| 713 |
-
null)
|
| 714 |
-
->order('quote_table.updated_at DESC');
|
| 715 |
-
}
|
| 716 |
-
|
| 717 |
-
//$collection->printlogquery(true,true);
|
| 718 |
-
$collection->load();
|
| 719 |
-
|
| 720 |
-
// Skip the rest of the code if the collection is empty
|
| 721 |
-
if ($collection->getSize() == 0) continue;
|
| 722 |
-
|
| 723 |
-
// Call iterator walk method with collection query string and callback method as parameters
|
| 724 |
-
// Has to be used to handle massive collection instead of foreach
|
| 725 |
-
Mage::getSingleton('core/resource_iterator')->walk($collection->getSelect(), array(array($this, 'generateRecipients')));
|
| 726 |
-
}
|
| 727 |
-
}
|
| 728 |
-
}
|
| 729 |
-
|
| 730 |
-
// Send the emails
|
| 731 |
-
$this->_sendEmails($dryrun,$testemail);
|
| 732 |
-
}
|
| 733 |
-
|
| 734 |
-
Mage::app()->setCurrentStore($this->_originalStoreId);
|
| 735 |
-
}
|
| 736 |
-
catch (Exception $e)
|
| 737 |
-
{
|
| 738 |
-
Mage::app()->setCurrentStore($this->_originalStoreId);
|
| 739 |
-
Mage::helper('abandonedcarts')->log(__METHOD__ . " " . $e->getMessage());
|
| 740 |
-
}
|
| 741 |
-
}
|
| 742 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/code/community/DigitalPianism/Abandonedcarts/Model/Resource/Link.php
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Class DigitalPianism_Abandonedcarts_Model_Resource_Link
|
| 5 |
+
*/
|
| 6 |
+
class DigitalPianism_Abandonedcarts_Model_Resource_Link extends Mage_Core_Model_Mysql4_Abstract
|
| 7 |
+
{
|
| 8 |
+
|
| 9 |
+
protected function _construct()
|
| 10 |
+
{
|
| 11 |
+
$this->_init('abandonedcarts/link', 'link_id');
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
}
|
app/code/community/DigitalPianism/Abandonedcarts/Model/Resource/Link/Collection.php
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Class DigitalPianism_Abandonedcarts_Model_Resource_Link_Collection
|
| 5 |
+
*/
|
| 6 |
+
class DigitalPianism_Abandonedcarts_Model_Resource_Link_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract {
|
| 7 |
+
|
| 8 |
+
public function _construct()
|
| 9 |
+
{
|
| 10 |
+
parent::_construct();
|
| 11 |
+
$this->_init('abandonedcarts/link');
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
}
|
app/code/community/DigitalPianism/Abandonedcarts/Model/Resource/Log.php
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Class DigitalPianism_Abandonedcarts_Model_Resource_Log
|
| 5 |
+
*/
|
| 6 |
+
class DigitalPianism_Abandonedcarts_Model_Resource_Log extends Mage_Core_Model_Mysql4_Abstract
|
| 7 |
+
{
|
| 8 |
+
|
| 9 |
+
protected function _construct()
|
| 10 |
+
{
|
| 11 |
+
$this->_init('abandonedcarts/log', 'log_id');
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
}
|
app/code/community/DigitalPianism/Abandonedcarts/Model/Resource/Log/Collection.php
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Class DigitalPianism_Abandonedcarts_Model_Resource_Log_Collection
|
| 5 |
+
*/
|
| 6 |
+
class DigitalPianism_Abandonedcarts_Model_Resource_Log_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract {
|
| 7 |
+
|
| 8 |
+
public function _construct()
|
| 9 |
+
{
|
| 10 |
+
parent::_construct();
|
| 11 |
+
$this->_init('abandonedcarts/log');
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
}
|
app/code/community/DigitalPianism/Abandonedcarts/controllers/Adminhtml/AbandonedcartsController.php
CHANGED
|
@@ -12,21 +12,123 @@ class DigitalPianism_Abandonedcarts_Adminhtml_AbandonedcartsController extends M
|
|
| 12 |
*/
|
| 13 |
protected function _isAllowed()
|
| 14 |
{
|
| 15 |
-
return Mage::getSingleton('admin/session')->isAllowed('
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
}
|
| 17 |
|
| 18 |
/**
|
| 19 |
-
* Manually send the notifications
|
| 20 |
*
|
| 21 |
-
* @return void
|
| 22 |
*/
|
| 23 |
-
public function
|
| 24 |
-
{
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
}
|
| 32 |
}
|
| 12 |
*/
|
| 13 |
protected function _isAllowed()
|
| 14 |
{
|
| 15 |
+
return Mage::getSingleton('admin/session')->isAllowed('digitalpianism_menu/abandonedcarts');
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
protected function _initAction()
|
| 19 |
+
{
|
| 20 |
+
$this->loadLayout()
|
| 21 |
+
->_setActiveMenu('digitalpianism_menu/abandonedcarts');
|
| 22 |
+
|
| 23 |
+
return $this;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
public function logsAction()
|
| 27 |
+
{
|
| 28 |
+
$this->_initAction();
|
| 29 |
+
$this->_addContent($this->getLayout()->createBlock('abandonedcarts/adminhtml_logs'));
|
| 30 |
+
$this->renderLayout();
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
public function gridAction()
|
| 34 |
+
{
|
| 35 |
+
$this->loadLayout();
|
| 36 |
+
$this->renderLayout();
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
public function indexAction()
|
| 40 |
+
{
|
| 41 |
+
$this->_initAction();
|
| 42 |
+
$this->_addContent($this->getLayout()->createBlock('abandonedcarts/adminhtml_abandonedcarts'));
|
| 43 |
+
$this->renderLayout();
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
public function salegridAction()
|
| 47 |
+
{
|
| 48 |
+
$this->loadLayout();
|
| 49 |
+
$this->renderLayout();
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
public function saleAction()
|
| 53 |
+
{
|
| 54 |
+
$this->_initAction();
|
| 55 |
+
$this->_addContent($this->getLayout()->createBlock('abandonedcarts/adminhtml_saleabandonedcarts'));
|
| 56 |
+
$this->renderLayout();
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
public function notifyAllAction()
|
| 60 |
+
{
|
| 61 |
+
try {
|
| 62 |
+
$count = Mage::getModel('abandonedcarts/notifier')->sendAbandonedCartsEmail();
|
| 63 |
+
Mage::getSingleton('adminhtml/session')->addSuccess(
|
| 64 |
+
Mage::helper('abandonedcarts')->__(
|
| 65 |
+
'%sTotal of %d customer(s) were successfully notified', (Mage::helper('abandonedcarts')->getDryRun() ? "!DRY RUN! " : ""), $count
|
| 66 |
+
)
|
| 67 |
+
);
|
| 68 |
+
} catch (Exception $e) {
|
| 69 |
+
Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
|
| 70 |
+
}
|
| 71 |
+
$this->_redirect('*/*/index');
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
public function notifySaleAllAction()
|
| 75 |
+
{
|
| 76 |
+
try {
|
| 77 |
+
$count = Mage::getModel('abandonedcarts/notifier')->sendAbandonedCartsSaleEmail();
|
| 78 |
+
Mage::getSingleton('adminhtml/session')->addSuccess(
|
| 79 |
+
Mage::helper('abandonedcarts')->__(
|
| 80 |
+
'%sTotal of %d customer(s) were successfully notified', (Mage::helper('abandonedcarts')->getDryRun() ? "!DRY RUN! " : ""), $count
|
| 81 |
+
)
|
| 82 |
+
);
|
| 83 |
+
} catch (Exception $e) {
|
| 84 |
+
Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
|
| 85 |
+
}
|
| 86 |
+
$this->_redirect('*/*/sale');
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
/**
|
| 90 |
+
*
|
| 91 |
+
*/
|
| 92 |
+
public function notifySaleAction()
|
| 93 |
+
{
|
| 94 |
+
$emails = $this->getRequest()->getParam('abandonedcarts');
|
| 95 |
+
if (!is_array($emails)) {
|
| 96 |
+
Mage::getSingleton('adminhtml/session')->addError(Mage::helper('abandonedcarts')->__('Please select email(s)'));
|
| 97 |
+
} else {
|
| 98 |
+
try {
|
| 99 |
+
Mage::getModel('abandonedcarts/notifier')->sendAbandonedCartsSaleEmail(false, false, $emails);
|
| 100 |
+
Mage::getSingleton('adminhtml/session')->addSuccess(
|
| 101 |
+
Mage::helper('abandonedcarts')->__(
|
| 102 |
+
'%sTotal of %d customer(s) were successfully notified', (Mage::helper('abandonedcarts')->getDryRun() ? "!DRY RUN! " : ""), count($emails)
|
| 103 |
+
)
|
| 104 |
+
);
|
| 105 |
+
} catch (Exception $e) {
|
| 106 |
+
Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
|
| 107 |
+
}
|
| 108 |
+
}
|
| 109 |
+
$this->_redirect('*/*/sale');
|
| 110 |
}
|
| 111 |
|
| 112 |
/**
|
|
|
|
| 113 |
*
|
|
|
|
| 114 |
*/
|
| 115 |
+
public function notifyAction()
|
| 116 |
+
{
|
| 117 |
+
$emails = $this->getRequest()->getParam('abandonedcarts');
|
| 118 |
+
if (!is_array($emails)) {
|
| 119 |
+
Mage::getSingleton('adminhtml/session')->addError(Mage::helper('abandonedcarts')->__('Please select email(s)'));
|
| 120 |
+
} else {
|
| 121 |
+
try {
|
| 122 |
+
Mage::getModel('abandonedcarts/notifier')->sendAbandonedCartsEmail(false, false, null, $emails);
|
| 123 |
+
Mage::getSingleton('adminhtml/session')->addSuccess(
|
| 124 |
+
Mage::helper('abandonedcarts')->__(
|
| 125 |
+
'%sTotal of %d customer(s) were successfully notified', (Mage::helper('abandonedcarts')->getDryRun() ? "!DRY RUN! " : ""), count($emails)
|
| 126 |
+
)
|
| 127 |
+
);
|
| 128 |
+
} catch (Exception $e) {
|
| 129 |
+
Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
|
| 130 |
+
}
|
| 131 |
+
}
|
| 132 |
+
$this->_redirect('*/*/index');
|
| 133 |
}
|
| 134 |
}
|
app/code/community/DigitalPianism/Abandonedcarts/controllers/IndexController.php
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Class DigitalPianism_Abandonedcarts_IndexController
|
| 5 |
+
*/
|
| 6 |
+
class DigitalPianism_Abandonedcarts_IndexController extends Mage_Core_Controller_Front_Action {
|
| 7 |
+
|
| 8 |
+
/**
|
| 9 |
+
*
|
| 10 |
+
*/
|
| 11 |
+
public function indexAction()
|
| 12 |
+
{
|
| 13 |
+
// Get the token
|
| 14 |
+
if ($token = $this->getRequest()->getParam('token')) {
|
| 15 |
+
|
| 16 |
+
// Find corresponding hash entry in the database
|
| 17 |
+
$link = Mage::getResourceModel('abandonedcarts/link_collection')
|
| 18 |
+
->addFieldToSelect(array('link_id','customer_email'))
|
| 19 |
+
->addFieldToFilter('token_hash', hash('sha256', $token))
|
| 20 |
+
->setPageSize(1);
|
| 21 |
+
|
| 22 |
+
if ($link->getSize()) {
|
| 23 |
+
|
| 24 |
+
$customerEmail = $link->getFirstItem()->getCustomerEmail();
|
| 25 |
+
/**
|
| 26 |
+
* @TODO add an entry in the log
|
| 27 |
+
*/
|
| 28 |
+
// Delete so it's one use only
|
| 29 |
+
$link->getFirstItem()->delete();
|
| 30 |
+
|
| 31 |
+
/** @var Mage_Customer_Model_Customer $customer */
|
| 32 |
+
$customer = Mage::getModel('customer/customer')
|
| 33 |
+
->setWebsiteId(Mage::app()->getStore()->getWebsiteId());
|
| 34 |
+
|
| 35 |
+
// Load the customer
|
| 36 |
+
$customer->loadByEmail($customerEmail);
|
| 37 |
+
|
| 38 |
+
// Log the customer in
|
| 39 |
+
if ($customer->getId()) {
|
| 40 |
+
Mage::getSingleton('customer/session')->setCustomerAsLoggedIn($customer);
|
| 41 |
+
Mage::getSingleton('customer/session')->renewSession();
|
| 42 |
+
// Redirects to cart
|
| 43 |
+
$this->_redirect('checkout/cart');
|
| 44 |
+
}
|
| 45 |
+
}
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
$this->_redirect('checkout/cart');
|
| 49 |
+
}
|
| 50 |
+
}
|
app/code/community/DigitalPianism/Abandonedcarts/data/abandonedcarts_setup/data-upgrade-1.0.0-1.0.1.php
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
$installer = $this;
|
| 4 |
+
$installer->startSetup();
|
| 5 |
+
|
| 6 |
+
/** @var Mage_Core_Model_Config $coreConfig */
|
| 7 |
+
$coreConfig = Mage::getModel('core/config');
|
| 8 |
+
|
| 9 |
+
if ($data = Mage::getStoreConfig('abandonedcartsconfig/options/name')) {
|
| 10 |
+
$coreConfig->saveConfig('abandonedcartsconfig/email/name', $data);
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
if ($data = Mage::getStoreConfig('abandonedcartsconfig/options/email')) {
|
| 14 |
+
$coreConfig->saveConfig('abandonedcartsconfig/email/email', $data);
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
if ($data = Mage::getStoreConfig('abandonedcartsconfig/options/email_template')) {
|
| 18 |
+
$coreConfig->saveConfig('abandonedcartsconfig/email/email_template', $data);
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
if ($data = Mage::getStoreConfig('abandonedcartsconfig/options/email_template_sale')) {
|
| 22 |
+
$coreConfig->saveConfig('abandonedcartsconfig/email/email_template_sale', $data);
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
if ($data = Mage::getStoreConfig('abandonedcartsconfig/options/dryrun')) {
|
| 26 |
+
$coreConfig->saveConfig('abandonedcartsconfig/test/dryrun', $data);
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
if ($data = Mage::getStoreConfig('abandonedcartsconfig/options/testemail')) {
|
| 30 |
+
$coreConfig->saveConfig('abandonedcartsconfig/test/testemail', $data);
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
$installer->endSetup();
|
app/code/community/DigitalPianism/Abandonedcarts/etc/adminhtml.xml
CHANGED
|
@@ -1,5 +1,39 @@
|
|
| 1 |
<?xml version="1.0"?>
|
| 2 |
<config>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
<acl>
|
| 4 |
<resources>
|
| 5 |
<all>
|
|
@@ -7,6 +41,32 @@
|
|
| 7 |
</all>
|
| 8 |
<admin>
|
| 9 |
<children>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
<system>
|
| 11 |
<children>
|
| 12 |
<config>
|
| 1 |
<?xml version="1.0"?>
|
| 2 |
<config>
|
| 3 |
+
<menu>
|
| 4 |
+
<digitalpianism_menu translate="title">
|
| 5 |
+
<title>Digital Pianism</title>
|
| 6 |
+
<sort_order>110</sort_order>
|
| 7 |
+
<children>
|
| 8 |
+
<abandonedcarts translate="title" module="abandonedcarts">
|
| 9 |
+
<title>Abandoned Carts</title>
|
| 10 |
+
<sort_order>1</sort_order>
|
| 11 |
+
<children>
|
| 12 |
+
<logs translate="title" module="abandonedcarts">
|
| 13 |
+
<title>Logs</title>
|
| 14 |
+
<sort_order>1</sort_order>
|
| 15 |
+
<action>adminhtml/abandonedcarts/logs</action>
|
| 16 |
+
</logs>
|
| 17 |
+
<list translate="title" module="abandonedcarts">
|
| 18 |
+
<title>Abandoned Carts List</title>
|
| 19 |
+
<sort_order>2</sort_order>
|
| 20 |
+
<action>adminhtml/abandonedcarts/index</action>
|
| 21 |
+
</list>
|
| 22 |
+
<sale_list translate="title" module="abandonedcarts">
|
| 23 |
+
<title>Abandoned Carts Sale List</title>
|
| 24 |
+
<sort_order>3</sort_order>
|
| 25 |
+
<action>adminhtml/abandonedcarts/sale</action>
|
| 26 |
+
</sale_list>
|
| 27 |
+
<config translate="title" module="abandonedcarts">
|
| 28 |
+
<title>Configuration</title>
|
| 29 |
+
<sort_order>4</sort_order>
|
| 30 |
+
<action>adminhtml/system_config/edit/section/abandonedcartsconfig</action>
|
| 31 |
+
</config>
|
| 32 |
+
</children>
|
| 33 |
+
</abandonedcarts>
|
| 34 |
+
</children>
|
| 35 |
+
</digitalpianism_menu>
|
| 36 |
+
</menu>
|
| 37 |
<acl>
|
| 38 |
<resources>
|
| 39 |
<all>
|
| 41 |
</all>
|
| 42 |
<admin>
|
| 43 |
<children>
|
| 44 |
+
<digitalpianism_menu>
|
| 45 |
+
<children>
|
| 46 |
+
<abandonedcarts translate="title">
|
| 47 |
+
<title>Abandoned Carts</title>
|
| 48 |
+
<sort_order>1</sort_order>
|
| 49 |
+
<children>
|
| 50 |
+
<logs translate="title">
|
| 51 |
+
<title>Logs</title>
|
| 52 |
+
<sort_order>1</sort_order>
|
| 53 |
+
</logs>
|
| 54 |
+
<list translate="title" module="abandonedcarts">
|
| 55 |
+
<title>Abandoned Carts List</title>
|
| 56 |
+
<sort_order>2</sort_order>
|
| 57 |
+
</list>
|
| 58 |
+
<sale_list translate="title" module="abandonedcarts">
|
| 59 |
+
<title>Abandoned Carts Sale List</title>
|
| 60 |
+
<sort_order>3</sort_order>
|
| 61 |
+
</sale_list>
|
| 62 |
+
<config translate="title">
|
| 63 |
+
<title>Configuration</title>
|
| 64 |
+
<sort_order>4</sort_order>
|
| 65 |
+
</config>
|
| 66 |
+
</children>
|
| 67 |
+
</abandonedcarts>
|
| 68 |
+
</children>
|
| 69 |
+
</digitalpianism_menu>
|
| 70 |
<system>
|
| 71 |
<children>
|
| 72 |
<config>
|
app/code/community/DigitalPianism/Abandonedcarts/etc/config.xml
CHANGED
|
@@ -4,7 +4,7 @@
|
|
| 4 |
|
| 5 |
<modules>
|
| 6 |
<DigitalPianism_Abandonedcarts>
|
| 7 |
-
<version>0.
|
| 8 |
</DigitalPianism_Abandonedcarts>
|
| 9 |
</modules>
|
| 10 |
|
|
@@ -33,6 +33,20 @@
|
|
| 33 |
</DigitalPianism_Abandonedcarts>
|
| 34 |
</modules>
|
| 35 |
</translate>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
</frontend>
|
| 37 |
|
| 38 |
<adminhtml>
|
|
@@ -46,6 +60,46 @@
|
|
| 46 |
</DigitalPianism_Abandonedcarts>
|
| 47 |
</modules>
|
| 48 |
</translate>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
</adminhtml>
|
| 50 |
|
| 51 |
<global>
|
|
@@ -64,7 +118,22 @@
|
|
| 64 |
<models>
|
| 65 |
<abandonedcarts>
|
| 66 |
<class>DigitalPianism_Abandonedcarts_Model</class>
|
|
|
|
| 67 |
</abandonedcarts>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
<sales_resource>
|
| 69 |
<rewrite>
|
| 70 |
<quote>DigitalPianism_Abandonedcarts_Model_Sales_Resource_Quote</quote>
|
|
@@ -117,7 +186,7 @@
|
|
| 117 |
<config_path>abandonedcartsconfig/options/cron_expr</config_path>
|
| 118 |
</schedule>
|
| 119 |
<run>
|
| 120 |
-
<model>abandonedcarts/
|
| 121 |
</run>
|
| 122 |
</digitalpianism_abandonedcarts_send>
|
| 123 |
<digitalpianism_abandonedcarts_sendsale>
|
|
@@ -125,21 +194,41 @@
|
|
| 125 |
<config_path>abandonedcartsconfig/options/cron_expr</config_path>
|
| 126 |
</schedule>
|
| 127 |
<run>
|
| 128 |
-
<model>abandonedcarts/
|
| 129 |
</run>
|
| 130 |
</digitalpianism_abandonedcarts_sendsale>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
</jobs>
|
| 132 |
</crontab>
|
| 133 |
|
| 134 |
<default>
|
| 135 |
<abandonedcartsconfig>
|
| 136 |
<options>
|
|
|
|
| 137 |
<cron_expr>0 1 * * *</cron_expr>
|
| 138 |
<notify_delay>20</notify_delay>
|
| 139 |
-
<
|
| 140 |
-
<email_template_sale>abandonedcartsconfig_options_email_template_sale</email_template_sale>
|
| 141 |
<customer_groups>1,2,3</customer_groups>
|
| 142 |
</options>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
</abandonedcartsconfig>
|
| 144 |
</default>
|
| 145 |
|
| 4 |
|
| 5 |
<modules>
|
| 6 |
<DigitalPianism_Abandonedcarts>
|
| 7 |
+
<version>1.0.0</version>
|
| 8 |
</DigitalPianism_Abandonedcarts>
|
| 9 |
</modules>
|
| 10 |
|
| 33 |
</DigitalPianism_Abandonedcarts>
|
| 34 |
</modules>
|
| 35 |
</translate>
|
| 36 |
+
|
| 37 |
+
<secure_url>
|
| 38 |
+
<abandonedcarts>/abandonedcarts/</abandonedcarts>
|
| 39 |
+
</secure_url>
|
| 40 |
+
|
| 41 |
+
<routers>
|
| 42 |
+
<abandonedcarts>
|
| 43 |
+
<use>standard</use>
|
| 44 |
+
<args>
|
| 45 |
+
<module>DigitalPianism_Abandonedcarts</module>
|
| 46 |
+
<frontName>abandonedcarts</frontName>
|
| 47 |
+
</args>
|
| 48 |
+
</abandonedcarts>
|
| 49 |
+
</routers>
|
| 50 |
</frontend>
|
| 51 |
|
| 52 |
<adminhtml>
|
| 60 |
</DigitalPianism_Abandonedcarts>
|
| 61 |
</modules>
|
| 62 |
</translate>
|
| 63 |
+
|
| 64 |
+
<layout>
|
| 65 |
+
<updates>
|
| 66 |
+
<abandonedcarts module="DigitalPianism_Abandonedcarts">
|
| 67 |
+
<file>digitalpianism/abandonedcarts.xml</file>
|
| 68 |
+
</abandonedcarts>
|
| 69 |
+
</updates>
|
| 70 |
+
</layout>
|
| 71 |
+
|
| 72 |
+
<events>
|
| 73 |
+
<!-- To register the controller action -->
|
| 74 |
+
<controller_action_predispatch_adminhtml>
|
| 75 |
+
<observers>
|
| 76 |
+
<digitalpianism_abandonedcarts_predispatch_register>
|
| 77 |
+
<type>singleton</type>
|
| 78 |
+
<class>abandonedcarts/adminhtml_observer</class>
|
| 79 |
+
<method>registerController</method>
|
| 80 |
+
</digitalpianism_abandonedcarts_predispatch_register>
|
| 81 |
+
</observers>
|
| 82 |
+
</controller_action_predispatch_adminhtml>
|
| 83 |
+
<!-- Called after creating a block -->
|
| 84 |
+
<core_layout_block_create_after>
|
| 85 |
+
<observers>
|
| 86 |
+
<digitalpianism_abandonedcarts_block_create_after>
|
| 87 |
+
<type>singleton</type>
|
| 88 |
+
<class>abandonedcarts/adminhtml_observer</class>
|
| 89 |
+
<method>addExtraColumnsToGrid</method>
|
| 90 |
+
</digitalpianism_abandonedcarts_block_create_after>
|
| 91 |
+
</observers>
|
| 92 |
+
</core_layout_block_create_after>
|
| 93 |
+
<!-- Called before loading a non EAV collection -->
|
| 94 |
+
<core_collection_abstract_load_before>
|
| 95 |
+
<observers>
|
| 96 |
+
<digitalpianism_abandonedcarts_before_core_load_collection>
|
| 97 |
+
<class>abandonedcarts/adminhtml_observer</class>
|
| 98 |
+
<method>addExtraColumnsToCollection</method>
|
| 99 |
+
</digitalpianism_abandonedcarts_before_core_load_collection>
|
| 100 |
+
</observers>
|
| 101 |
+
</core_collection_abstract_load_before>
|
| 102 |
+
</events>
|
| 103 |
</adminhtml>
|
| 104 |
|
| 105 |
<global>
|
| 118 |
<models>
|
| 119 |
<abandonedcarts>
|
| 120 |
<class>DigitalPianism_Abandonedcarts_Model</class>
|
| 121 |
+
<resourceModel>abandonedcarts_mysql4</resourceModel>
|
| 122 |
</abandonedcarts>
|
| 123 |
+
|
| 124 |
+
<abandonedcarts_mysql4>
|
| 125 |
+
<class>DigitalPianism_Abandonedcarts_Model_Resource</class>
|
| 126 |
+
<!-- declare table test -->
|
| 127 |
+
<entities>
|
| 128 |
+
<log>
|
| 129 |
+
<table>dp_abandonedcarts_log</table>
|
| 130 |
+
</log>
|
| 131 |
+
<link>
|
| 132 |
+
<table>dp_abandonedcarts_link</table>
|
| 133 |
+
</link>
|
| 134 |
+
</entities>
|
| 135 |
+
</abandonedcarts_mysql4>
|
| 136 |
+
|
| 137 |
<sales_resource>
|
| 138 |
<rewrite>
|
| 139 |
<quote>DigitalPianism_Abandonedcarts_Model_Sales_Resource_Quote</quote>
|
| 186 |
<config_path>abandonedcartsconfig/options/cron_expr</config_path>
|
| 187 |
</schedule>
|
| 188 |
<run>
|
| 189 |
+
<model>abandonedcarts/notifier::sendAbandonedCartsEmail</model>
|
| 190 |
</run>
|
| 191 |
</digitalpianism_abandonedcarts_send>
|
| 192 |
<digitalpianism_abandonedcarts_sendsale>
|
| 194 |
<config_path>abandonedcartsconfig/options/cron_expr</config_path>
|
| 195 |
</schedule>
|
| 196 |
<run>
|
| 197 |
+
<model>abandonedcarts/notifier::sendAbandonedCartsSaleEmail</model>
|
| 198 |
</run>
|
| 199 |
</digitalpianism_abandonedcarts_sendsale>
|
| 200 |
+
<digitalpianism_abandonedcarts_deleteexpiredlinks>
|
| 201 |
+
<schedule>
|
| 202 |
+
<cron_expr>*/5 * * * *</cron_expr>
|
| 203 |
+
</schedule>
|
| 204 |
+
<run>
|
| 205 |
+
<model>abandonedcarts/link_cleaner::cleanExpiredLinks</model>
|
| 206 |
+
</run>
|
| 207 |
+
</digitalpianism_abandonedcarts_deleteexpiredlinks>
|
| 208 |
</jobs>
|
| 209 |
</crontab>
|
| 210 |
|
| 211 |
<default>
|
| 212 |
<abandonedcartsconfig>
|
| 213 |
<options>
|
| 214 |
+
<enable>0</enable>
|
| 215 |
<cron_expr>0 1 * * *</cron_expr>
|
| 216 |
<notify_delay>20</notify_delay>
|
| 217 |
+
<enable_sale>1</enable_sale>
|
|
|
|
| 218 |
<customer_groups>1,2,3</customer_groups>
|
| 219 |
</options>
|
| 220 |
+
<email>
|
| 221 |
+
<email_template>abandonedcartsconfig_options_email_template</email_template>
|
| 222 |
+
<email_template_sale>abandonedcartsconfig_options_email_template_sale</email_template_sale>
|
| 223 |
+
<autologin>0</autologin>
|
| 224 |
+
</email>
|
| 225 |
+
<campaign>
|
| 226 |
+
<enable>0</enable>
|
| 227 |
+
</campaign>
|
| 228 |
+
<test>
|
| 229 |
+
<dryrun>0</dryrun>
|
| 230 |
+
<log>0</log>
|
| 231 |
+
</test>
|
| 232 |
</abandonedcartsconfig>
|
| 233 |
</default>
|
| 234 |
|
app/code/community/DigitalPianism/Abandonedcarts/etc/system.xml
CHANGED
|
@@ -16,9 +16,9 @@
|
|
| 16 |
<show_in_store>1</show_in_store>
|
| 17 |
<groups>
|
| 18 |
<options translate="label">
|
| 19 |
-
<label>
|
| 20 |
<frontend_type>text</frontend_type>
|
| 21 |
-
<sort_order>
|
| 22 |
<show_in_default>1</show_in_default>
|
| 23 |
<show_in_website>1</show_in_website>
|
| 24 |
<show_in_store>1</show_in_store>
|
|
@@ -26,26 +26,66 @@
|
|
| 26 |
<enable translate="label">
|
| 27 |
<label>Enable Abandoned Carts Notification</label>
|
| 28 |
<frontend_type>select</frontend_type>
|
| 29 |
-
|
| 30 |
<sort_order>10</sort_order>
|
| 31 |
<show_in_default>1</show_in_default>
|
| 32 |
<show_in_website>1</show_in_website>
|
| 33 |
<show_in_store>1</show_in_store>
|
| 34 |
</enable>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
<cron_expr translate="label">
|
| 36 |
<label>Cron Schedule</label>
|
| 37 |
<frontend_type>text</frontend_type>
|
| 38 |
-
<sort_order>
|
| 39 |
<show_in_default>1</show_in_default>
|
| 40 |
<show_in_website>1</show_in_website>
|
| 41 |
<show_in_store>1</show_in_store>
|
| 42 |
</cron_expr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
<name translate="label">
|
| 44 |
<label>Sender Name</label>
|
| 45 |
<frontend_type>text</frontend_type>
|
| 46 |
<backend_model>adminhtml/system_config_backend_email_sender</backend_model>
|
| 47 |
-
<validate>validate-emailSender</validate>
|
| 48 |
-
<sort_order>
|
| 49 |
<show_in_default>1</show_in_default>
|
| 50 |
<show_in_website>1</show_in_website>
|
| 51 |
<show_in_store>1</show_in_store>
|
|
@@ -55,7 +95,7 @@
|
|
| 55 |
<frontend_type>text</frontend_type>
|
| 56 |
<validate>validate-email</validate>
|
| 57 |
<backend_model>adminhtml/system_config_backend_email_address</backend_model>
|
| 58 |
-
<sort_order>
|
| 59 |
<show_in_default>1</show_in_default>
|
| 60 |
<show_in_website>1</show_in_website>
|
| 61 |
<show_in_store>1</show_in_store>
|
|
@@ -64,78 +104,101 @@
|
|
| 64 |
<label>Email Template for Unaltered Abandoned Carts</label>
|
| 65 |
<frontend_type>select</frontend_type>
|
| 66 |
<source_model>adminhtml/system_config_source_email_template</source_model>
|
| 67 |
-
<sort_order>
|
| 68 |
<show_in_default>1</show_in_default>
|
| 69 |
<show_in_website>1</show_in_website>
|
| 70 |
<show_in_store>1</show_in_store>
|
| 71 |
</email_template>
|
| 72 |
-
<
|
| 73 |
-
<label>
|
| 74 |
-
<frontend_type>
|
| 75 |
-
<
|
| 76 |
-
<sort_order>
|
| 77 |
<show_in_default>1</show_in_default>
|
| 78 |
<show_in_website>1</show_in_website>
|
| 79 |
<show_in_store>1</show_in_store>
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
<label>Enable Sale Abandoned Carts Notification</label>
|
| 84 |
<frontend_type>select</frontend_type>
|
| 85 |
-
|
| 86 |
-
<sort_order>
|
| 87 |
<show_in_default>1</show_in_default>
|
| 88 |
<show_in_website>1</show_in_website>
|
| 89 |
<show_in_store>1</show_in_store>
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
<frontend_type>select</frontend_type>
|
| 94 |
-
<source_model>adminhtml/
|
| 95 |
-
<sort_order>
|
| 96 |
<show_in_default>1</show_in_default>
|
| 97 |
<show_in_website>1</show_in_website>
|
| 98 |
<show_in_store>1</show_in_store>
|
| 99 |
-
</
|
| 100 |
-
<
|
| 101 |
-
<label>
|
| 102 |
-
<frontend_type>
|
| 103 |
-
<
|
| 104 |
-
<sort_order>75</sort_order>
|
| 105 |
<show_in_default>1</show_in_default>
|
| 106 |
<show_in_website>1</show_in_website>
|
| 107 |
<show_in_store>1</show_in_store>
|
| 108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
<dryrun translate="label comment">
|
| 110 |
<label>Dry Run</label>
|
| 111 |
<frontend_type>select</frontend_type>
|
| 112 |
-
|
| 113 |
-
<sort_order>
|
| 114 |
<show_in_default>1</show_in_default>
|
| 115 |
<show_in_website>1</show_in_website>
|
| 116 |
<show_in_store>1</show_in_store>
|
| 117 |
-
<comment>Setting this parameter to Yes will
|
| 118 |
</dryrun>
|
| 119 |
<testemail translate="label comment">
|
| 120 |
<label>Test Email</label>
|
| 121 |
<frontend_type>text</frontend_type>
|
| 122 |
-
<sort_order>
|
| 123 |
<show_in_default>1</show_in_default>
|
| 124 |
<show_in_website>1</show_in_website>
|
| 125 |
<show_in_store>1</show_in_store>
|
| 126 |
-
<
|
|
|
|
| 127 |
</testemail>
|
| 128 |
-
<
|
| 129 |
-
<label>
|
| 130 |
-
<frontend_type>
|
| 131 |
-
<
|
| 132 |
-
<sort_order>
|
| 133 |
<show_in_default>1</show_in_default>
|
| 134 |
<show_in_website>1</show_in_website>
|
| 135 |
<show_in_store>1</show_in_store>
|
| 136 |
-
|
|
|
|
| 137 |
</fields>
|
| 138 |
-
</
|
| 139 |
</groups>
|
| 140 |
</abandonedcartsconfig>
|
| 141 |
</sections>
|
| 16 |
<show_in_store>1</show_in_store>
|
| 17 |
<groups>
|
| 18 |
<options translate="label">
|
| 19 |
+
<label>Global Configuration</label>
|
| 20 |
<frontend_type>text</frontend_type>
|
| 21 |
+
<sort_order>1</sort_order>
|
| 22 |
<show_in_default>1</show_in_default>
|
| 23 |
<show_in_website>1</show_in_website>
|
| 24 |
<show_in_store>1</show_in_store>
|
| 26 |
<enable translate="label">
|
| 27 |
<label>Enable Abandoned Carts Notification</label>
|
| 28 |
<frontend_type>select</frontend_type>
|
| 29 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
| 30 |
<sort_order>10</sort_order>
|
| 31 |
<show_in_default>1</show_in_default>
|
| 32 |
<show_in_website>1</show_in_website>
|
| 33 |
<show_in_store>1</show_in_store>
|
| 34 |
</enable>
|
| 35 |
+
<enable_sale translate="label">
|
| 36 |
+
<label>Enable Sale Abandoned Carts Notification</label>
|
| 37 |
+
<frontend_type>select</frontend_type>
|
| 38 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
| 39 |
+
<sort_order>20</sort_order>
|
| 40 |
+
<show_in_default>1</show_in_default>
|
| 41 |
+
<show_in_website>1</show_in_website>
|
| 42 |
+
<show_in_store>1</show_in_store>
|
| 43 |
+
</enable_sale>
|
| 44 |
<cron_expr translate="label">
|
| 45 |
<label>Cron Schedule</label>
|
| 46 |
<frontend_type>text</frontend_type>
|
| 47 |
+
<sort_order>30</sort_order>
|
| 48 |
<show_in_default>1</show_in_default>
|
| 49 |
<show_in_website>1</show_in_website>
|
| 50 |
<show_in_store>1</show_in_store>
|
| 51 |
</cron_expr>
|
| 52 |
+
<notify_delay translate="label comment">
|
| 53 |
+
<label>Delay / Send Abandoned Cart Email After</label>
|
| 54 |
+
<frontend_type>text</frontend_type>
|
| 55 |
+
<validate>validate-not-negative-number</validate>
|
| 56 |
+
<sort_order>40</sort_order>
|
| 57 |
+
<show_in_default>1</show_in_default>
|
| 58 |
+
<show_in_website>1</show_in_website>
|
| 59 |
+
<show_in_store>1</show_in_store>
|
| 60 |
+
<depends><enable>1</enable></depends>
|
| 61 |
+
<comment>(days). NB: this only affects unaltered abandoned carts. If products go on sale, the email below is sent on the same day.</comment>
|
| 62 |
+
</notify_delay>
|
| 63 |
+
<customer_groups translate="label">
|
| 64 |
+
<label>Customer Groups Restriction</label>
|
| 65 |
+
<frontend_type>multiselect</frontend_type>
|
| 66 |
+
<source_model>adminhtml/system_config_source_customer_group_multiselect</source_model>
|
| 67 |
+
<sort_order>50</sort_order>
|
| 68 |
+
<show_in_default>1</show_in_default>
|
| 69 |
+
<show_in_website>1</show_in_website>
|
| 70 |
+
<show_in_store>1</show_in_store>
|
| 71 |
+
<depends><enable>1</enable></depends>
|
| 72 |
+
</customer_groups>
|
| 73 |
+
</fields>
|
| 74 |
+
</options>
|
| 75 |
+
<email translate="label">
|
| 76 |
+
<label>Email Configuration</label>
|
| 77 |
+
<frontend_type>text</frontend_type>
|
| 78 |
+
<sort_order>2</sort_order>
|
| 79 |
+
<show_in_default>1</show_in_default>
|
| 80 |
+
<show_in_website>1</show_in_website>
|
| 81 |
+
<show_in_store>1</show_in_store>
|
| 82 |
+
<fields>
|
| 83 |
<name translate="label">
|
| 84 |
<label>Sender Name</label>
|
| 85 |
<frontend_type>text</frontend_type>
|
| 86 |
<backend_model>adminhtml/system_config_backend_email_sender</backend_model>
|
| 87 |
+
<validate>validate-emailSender</validate>
|
| 88 |
+
<sort_order>10</sort_order>
|
| 89 |
<show_in_default>1</show_in_default>
|
| 90 |
<show_in_website>1</show_in_website>
|
| 91 |
<show_in_store>1</show_in_store>
|
| 95 |
<frontend_type>text</frontend_type>
|
| 96 |
<validate>validate-email</validate>
|
| 97 |
<backend_model>adminhtml/system_config_backend_email_address</backend_model>
|
| 98 |
+
<sort_order>20</sort_order>
|
| 99 |
<show_in_default>1</show_in_default>
|
| 100 |
<show_in_website>1</show_in_website>
|
| 101 |
<show_in_store>1</show_in_store>
|
| 104 |
<label>Email Template for Unaltered Abandoned Carts</label>
|
| 105 |
<frontend_type>select</frontend_type>
|
| 106 |
<source_model>adminhtml/system_config_source_email_template</source_model>
|
| 107 |
+
<sort_order>30</sort_order>
|
| 108 |
<show_in_default>1</show_in_default>
|
| 109 |
<show_in_website>1</show_in_website>
|
| 110 |
<show_in_store>1</show_in_store>
|
| 111 |
</email_template>
|
| 112 |
+
<email_template_sale translate="label">
|
| 113 |
+
<label>Email Template for Abandoned Carts Sale</label>
|
| 114 |
+
<frontend_type>select</frontend_type>
|
| 115 |
+
<source_model>adminhtml/system_config_source_email_template</source_model>
|
| 116 |
+
<sort_order>40</sort_order>
|
| 117 |
<show_in_default>1</show_in_default>
|
| 118 |
<show_in_website>1</show_in_website>
|
| 119 |
<show_in_store>1</show_in_store>
|
| 120 |
+
</email_template_sale>
|
| 121 |
+
<autologin translate="label comment">
|
| 122 |
+
<label>Enable Automatic login link in the email</label>
|
|
|
|
| 123 |
<frontend_type>select</frontend_type>
|
| 124 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
| 125 |
+
<sort_order>50</sort_order>
|
| 126 |
<show_in_default>1</show_in_default>
|
| 127 |
<show_in_website>1</show_in_website>
|
| 128 |
<show_in_store>1</show_in_store>
|
| 129 |
+
<comment>Autologin links are one use only and expire after 48 hours for security reasons.</comment>
|
| 130 |
+
</autologin>
|
| 131 |
+
</fields>
|
| 132 |
+
</email>
|
| 133 |
+
<campaign translate="label">
|
| 134 |
+
<label>Google Campaign Tracker</label>
|
| 135 |
+
<frontend_type>text</frontend_type>
|
| 136 |
+
<sort_order>3</sort_order>
|
| 137 |
+
<show_in_default>1</show_in_default>
|
| 138 |
+
<show_in_website>1</show_in_website>
|
| 139 |
+
<show_in_store>1</show_in_store>
|
| 140 |
+
<fields>
|
| 141 |
+
<enable translate="label comment">
|
| 142 |
+
<label>Enable</label>
|
| 143 |
<frontend_type>select</frontend_type>
|
| 144 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
| 145 |
+
<sort_order>10</sort_order>
|
| 146 |
<show_in_default>1</show_in_default>
|
| 147 |
<show_in_website>1</show_in_website>
|
| 148 |
<show_in_store>1</show_in_store>
|
| 149 |
+
</enable>
|
| 150 |
+
<name translate="label comment">
|
| 151 |
+
<label>Campaign Name</label>
|
| 152 |
+
<frontend_type>text</frontend_type>
|
| 153 |
+
<sort_order>20</sort_order>
|
|
|
|
| 154 |
<show_in_default>1</show_in_default>
|
| 155 |
<show_in_website>1</show_in_website>
|
| 156 |
<show_in_store>1</show_in_store>
|
| 157 |
+
<depends><enable>1</enable></depends>
|
| 158 |
+
<comment>utm_campaign</comment>
|
| 159 |
+
</name>
|
| 160 |
+
</fields>
|
| 161 |
+
</campaign>
|
| 162 |
+
<test translate="label">
|
| 163 |
+
<label>Test and debug</label>
|
| 164 |
+
<frontend_type>text</frontend_type>
|
| 165 |
+
<sort_order>3</sort_order>
|
| 166 |
+
<show_in_default>1</show_in_default>
|
| 167 |
+
<show_in_website>1</show_in_website>
|
| 168 |
+
<show_in_store>1</show_in_store>
|
| 169 |
+
<fields>
|
| 170 |
<dryrun translate="label comment">
|
| 171 |
<label>Dry Run</label>
|
| 172 |
<frontend_type>select</frontend_type>
|
| 173 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
| 174 |
+
<sort_order>10</sort_order>
|
| 175 |
<show_in_default>1</show_in_default>
|
| 176 |
<show_in_website>1</show_in_website>
|
| 177 |
<show_in_store>1</show_in_store>
|
| 178 |
+
<comment>Setting this parameter to Yes will not send the real email notification.</comment>
|
| 179 |
</dryrun>
|
| 180 |
<testemail translate="label comment">
|
| 181 |
<label>Test Email</label>
|
| 182 |
<frontend_type>text</frontend_type>
|
| 183 |
+
<sort_order>20</sort_order>
|
| 184 |
<show_in_default>1</show_in_default>
|
| 185 |
<show_in_website>1</show_in_website>
|
| 186 |
<show_in_store>1</show_in_store>
|
| 187 |
+
<depends><dryrun>1</dryrun></depends>
|
| 188 |
+
<comment>With dry run set to yes, this email is used as a recipient email replacement so the real customer don't receive the email.</comment>
|
| 189 |
</testemail>
|
| 190 |
+
<log translate="label comment">
|
| 191 |
+
<label>Enable Log</label>
|
| 192 |
+
<frontend_type>select</frontend_type>
|
| 193 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
| 194 |
+
<sort_order>30</sort_order>
|
| 195 |
<show_in_default>1</show_in_default>
|
| 196 |
<show_in_website>1</show_in_website>
|
| 197 |
<show_in_store>1</show_in_store>
|
| 198 |
+
<comment>Setting this parameter to Yes will log abandoned cart actions so it can be viewed in Digital Pianism > Abandoned Carts > Logs</comment>
|
| 199 |
+
</log>
|
| 200 |
</fields>
|
| 201 |
+
</test>
|
| 202 |
</groups>
|
| 203 |
</abandonedcartsconfig>
|
| 204 |
</sections>
|
app/code/community/DigitalPianism/Abandonedcarts/sql/abandonedcarts_setup/upgrade-0.3.6-1.0.0.php
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
$installer = $this;
|
| 4 |
+
|
| 5 |
+
$installer->startSetup();
|
| 6 |
+
|
| 7 |
+
$installer->run("
|
| 8 |
+
CREATE TABLE IF NOT EXISTS {$this->getTable('abandonedcarts/log')} (
|
| 9 |
+
`log_id` int(11) NOT NULL auto_increment,
|
| 10 |
+
`customer_email` varchar(255) default NULL,
|
| 11 |
+
`type` int(11) default NULL,
|
| 12 |
+
`comment` text default NULL,
|
| 13 |
+
`store` int(11) default NULL,
|
| 14 |
+
`dryrun` varchar(255) default NULL,
|
| 15 |
+
`added` timestamp NOT NULL default CURRENT_TIMESTAMP,
|
| 16 |
+
PRIMARY KEY (`log_id`)
|
| 17 |
+
) ENGINE = InnoDB DEFAULT CHARSET = utf8;
|
| 18 |
+
|
| 19 |
+
CREATE TABLE IF NOT EXISTS {$this->getTable('abandonedcarts/link')} (
|
| 20 |
+
`link_id` int(11) NOT NULL auto_increment,
|
| 21 |
+
`token_hash` text default NULL,
|
| 22 |
+
`customer_email` varchar(255) default NULL,
|
| 23 |
+
`expiration_date` datetime default NULL,
|
| 24 |
+
PRIMARY KEY (`link_id`)
|
| 25 |
+
) ENGINE = InnoDB DEFAULT CHARSET = utf8;");
|
| 26 |
+
|
| 27 |
+
$installer->endSetup();
|
app/design/adminhtml/default/default/layout/digitalpianism/abandonedcarts.xml
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0"?>
|
| 2 |
+
<layout version="0.1.0">
|
| 3 |
+
<adminhtml_abandonedcarts_grid>
|
| 4 |
+
<block type="abandonedcarts/adminhtml_abandonedcarts_grid" name="root" output="toHtml"/>
|
| 5 |
+
</adminhtml_abandonedcarts_grid>
|
| 6 |
+
|
| 7 |
+
<adminhtml_abandonedcarts_salegrid>
|
| 8 |
+
<block type="abandonedcarts/adminhtml_saleabandonedcarts_grid" name="root" output="toHtml"/>
|
| 9 |
+
</adminhtml_abandonedcarts_salegrid>
|
| 10 |
+
</layout>
|
app/design/adminhtml/default/default/template/digitalpianism/abandonedcarts/list.phtml
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<div class="content-header">
|
| 2 |
+
<table cellspacing="0">
|
| 3 |
+
<tr>
|
| 4 |
+
<td style="<?php echo $this->getHeaderWidth() ?>"><?php echo $this->getHeaderHtml() ?></td>
|
| 5 |
+
<td class="form-buttons"><?php echo $this->getButtonsHtml() ?></td>
|
| 6 |
+
</tr>
|
| 7 |
+
</table>
|
| 8 |
+
</div>
|
| 9 |
+
<?php echo $this->getStoreSwitcherHtml() ?>
|
| 10 |
+
<div>
|
| 11 |
+
<?php echo $this->getGridHtml() ?>
|
| 12 |
+
</div>
|
app/design/adminhtml/default/default/template/digitalpianism/abandonedcarts/system/config/button.phtml
DELETED
|
@@ -1,17 +0,0 @@
|
|
| 1 |
-
<script type="text/javascript">
|
| 2 |
-
//<![CDATA[
|
| 3 |
-
function send() {
|
| 4 |
-
new Ajax.Request('<?php echo $this->getAjaxCheckUrl() ?>', {
|
| 5 |
-
method: 'get',
|
| 6 |
-
onSuccess: function(transport){
|
| 7 |
-
|
| 8 |
-
if (transport.responseText){
|
| 9 |
-
alert('Abandoned carts notifications have been sent')
|
| 10 |
-
}
|
| 11 |
-
}
|
| 12 |
-
});
|
| 13 |
-
}
|
| 14 |
-
//]]>
|
| 15 |
-
</script>
|
| 16 |
-
|
| 17 |
-
<?php echo $this->getButtonHtml() ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/design/frontend/base/default/template/digitalpianism/abandonedcarts/email/items.phtml
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
$productNames = $this->getProductname();
|
| 3 |
+
$productImages = $this->getProductimage();
|
| 4 |
+
?>
|
| 5 |
+
<table>
|
| 6 |
+
<tr>
|
| 7 |
+
<th>
|
| 8 |
+
<?php echo Mage::helper('abandonedcarts')->__('Product Image'); ?>
|
| 9 |
+
</th>
|
| 10 |
+
<th>
|
| 11 |
+
<?php echo Mage::helper('abandonedcarts')->__('Product Name'); ?>
|
| 12 |
+
</th>
|
| 13 |
+
</tr>
|
| 14 |
+
<?php foreach($productNames as $i => $name): ?>
|
| 15 |
+
<tr>
|
| 16 |
+
<td>
|
| 17 |
+
<?php if ($productImages[$i]): ?>
|
| 18 |
+
<img src="<?php echo $productImages[$i]; ?>" alt="<?php echo $name; ?>" />
|
| 19 |
+
<?php endif; ?>
|
| 20 |
+
</td>
|
| 21 |
+
<td>
|
| 22 |
+
<?php echo $name; ?>
|
| 23 |
+
</td>
|
| 24 |
+
</tr>
|
| 25 |
+
<?php endforeach; ?>
|
| 26 |
+
</table>
|
app/design/frontend/base/default/template/digitalpianism/abandonedcarts/email/sale_items.phtml
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
$productNames = $this->getProductname();
|
| 3 |
+
$productImages = $this->getProductimage();
|
| 4 |
+
$cartPrices = $this->getCartprice();
|
| 5 |
+
$salePrices = $this->getSpecialprice();
|
| 6 |
+
?>
|
| 7 |
+
<table>
|
| 8 |
+
<tr>
|
| 9 |
+
<th>
|
| 10 |
+
<?php echo Mage::helper('abandonedcarts')->__('Product Image'); ?>
|
| 11 |
+
</th>
|
| 12 |
+
<th>
|
| 13 |
+
<?php echo Mage::helper('abandonedcarts')->__('Product Name'); ?>
|
| 14 |
+
</th>
|
| 15 |
+
<th>
|
| 16 |
+
<?php echo Mage::helper('abandonedcarts')->__('Price when you left'); ?>
|
| 17 |
+
</th>
|
| 18 |
+
<th>
|
| 19 |
+
<?php echo Mage::helper('abandonedcarts')->__('Price now'); ?>
|
| 20 |
+
</th>
|
| 21 |
+
</tr>
|
| 22 |
+
<?php foreach($productNames as $i => $name): ?>
|
| 23 |
+
<tr>
|
| 24 |
+
<td>
|
| 25 |
+
<?php if ($productImages[$i]): ?>
|
| 26 |
+
<img src="<?php echo $productImages[$i]; ?>" alt="<?php echo $name; ?>" />
|
| 27 |
+
<?php endif; ?>
|
| 28 |
+
</td>
|
| 29 |
+
<td>
|
| 30 |
+
<?php echo $name; ?>
|
| 31 |
+
</td>
|
| 32 |
+
<td>
|
| 33 |
+
<?php echo $cartPrices[$i]; ?>
|
| 34 |
+
</td>
|
| 35 |
+
<td>
|
| 36 |
+
<?php echo $salePrices[$i]; ?>
|
| 37 |
+
</td>
|
| 38 |
+
</tr>
|
| 39 |
+
<?php endforeach; ?>
|
| 40 |
+
</table>
|
app/locale/en_US/DigitalPianism_Abandonedcarts.csv
CHANGED
|
@@ -3,15 +3,43 @@ Abandoned Carts Emails,Abandoned Carts Emails
|
|
| 3 |
Enable Abandoned Carts Notification,Enable Abandoned Carts Notification
|
| 4 |
Enable Sale Abandoned Carts Notification,Enable Sale Abandoned Carts Notification
|
| 5 |
Dry Run,Dry Run
|
| 6 |
-
Setting this parameter to Yes will log all the email addresses supposed to receive a notification into the var/log/digitalpianism_abandonedcarts.log file and will not send the real email notification,Setting this parameter to Yes will log all the email addresses supposed to receive a notification into the var/log/digitalpianism_abandonedcarts.log file and will not send the real email notification
|
| 7 |
Test Email,Test Email
|
| 8 |
-
With dry run set to yes, this email is used to filter the emails supposed to be sent and only send a notification email to the customer with this email address,With dry run set to yes, this email is used to filter the emails supposed to be sent and only send a notification email to the customer with this email address
|
| 9 |
Send Notifications Now,Send Notifications Now
|
| 10 |
Cron Schedule,Cron Schedule
|
| 11 |
Sender Name,Sender Name
|
| 12 |
Sender Email,Sender Email
|
| 13 |
Email Template for Unaltered Abandoned Carts,Email Template for Unaltered Abandoned Carts
|
| 14 |
-
Send Abandoned Cart Email After,Send Abandoned Cart Email After
|
| 15 |
(days). NB: this only affects unaltered abandoned carts. If products go on sale, the email below is sent on the same day.,(days). NB: this only affects unaltered abandoned carts. If products go on sale, the email below is sent on the same day.
|
| 16 |
Email Template for Sale Products,Email Template for Sale Products
|
| 17 |
-
Customer Groups Restriction,Customer Groups Restriction
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
Enable Abandoned Carts Notification,Enable Abandoned Carts Notification
|
| 4 |
Enable Sale Abandoned Carts Notification,Enable Sale Abandoned Carts Notification
|
| 5 |
Dry Run,Dry Run
|
|
|
|
| 6 |
Test Email,Test Email
|
|
|
|
| 7 |
Send Notifications Now,Send Notifications Now
|
| 8 |
Cron Schedule,Cron Schedule
|
| 9 |
Sender Name,Sender Name
|
| 10 |
Sender Email,Sender Email
|
| 11 |
Email Template for Unaltered Abandoned Carts,Email Template for Unaltered Abandoned Carts
|
|
|
|
| 12 |
(days). NB: this only affects unaltered abandoned carts. If products go on sale, the email below is sent on the same day.,(days). NB: this only affects unaltered abandoned carts. If products go on sale, the email below is sent on the same day.
|
| 13 |
Email Template for Sale Products,Email Template for Sale Products
|
| 14 |
+
Customer Groups Restriction,Customer Groups Restriction
|
| 15 |
+
With dry run set to yes, this email is used as a recipient email replacement so the real customer don't receive the email.,With dry run set to yes, this email is used as a recipient email replacement so the real customer don't receive the email.
|
| 16 |
+
Setting this parameter to Yes will not send the real email notification.,Setting this parameter to Yes will not send the real email notification.
|
| 17 |
+
Delay / Send Abandoned Cart Email After,Delay / Send Abandoned Cart Email After
|
| 18 |
+
Abandoned Carts (Applied delay: %s days),Abandoned Carts (Applied delay: %s days)
|
| 19 |
+
Send notifications,Send notifications
|
| 20 |
+
Abandoned Carts Logs,Abandoned Carts Logs
|
| 21 |
+
Sale Abandoned Carts,Sale Abandoned Carts
|
| 22 |
+
Customer Email,Customer Email
|
| 23 |
+
Customer Firstname,Customer Firstname
|
| 24 |
+
Customer Lastname,Customer Lastname
|
| 25 |
+
Product Ids,Product Ids
|
| 26 |
+
Product Names,Product Names
|
| 27 |
+
Cart Total,Cart Total
|
| 28 |
+
Cart Updated At,Cart Updated At
|
| 29 |
+
Send notification,Send notification
|
| 30 |
+
Type,Type
|
| 31 |
+
Comment,Comment
|
| 32 |
+
Store #,Store #
|
| 33 |
+
Cart Regular Total,Cart Regular Total
|
| 34 |
+
Cart Sale Total,Cart Sale Total
|
| 35 |
+
%sTotal of %d customer(s) were successfully notified,%sTotal of %d customer(s) were successfully notified
|
| 36 |
+
Logs,Logs
|
| 37 |
+
Abandoned Carts List,Abandoned Carts List
|
| 38 |
+
Abandoned Carts Sale List,Abandoned Carts Sale List
|
| 39 |
+
Enable Automatic login link in the email,Enable Automatic login link in the email
|
| 40 |
+
Autologin links are one use only and expire after 48 hours for security reasons.,Autologin links are one use only and expire after 48 hours for security reasons.
|
| 41 |
+
Google Campaign Tracker,Google Campaign Tracker
|
| 42 |
+
Campaign Name,Campaign Name
|
| 43 |
+
Test and debug,Test and debug
|
| 44 |
+
Enable Log,Enable Log
|
| 45 |
+
Setting this parameter to Yes will log abandoned cart actions so it can be viewed in Digital Pianism > Abandoned Carts > Logs,Setting this parameter to Yes will log abandoned cart actions so it can be viewed in Digital Pianism > Abandoned Carts > Logs
|
app/locale/en_US/template/email/digitalpianism/abandonedcarts/sales_abandonedcarts.html
CHANGED
|
@@ -14,11 +14,9 @@
|
|
| 14 |
<td valign="top">
|
| 15 |
<div>
|
| 16 |
<p>Dear {{var firstname}},</p>
|
| 17 |
-
<p>You have abandoned the following product in your shopping bag:</p>
|
| 18 |
-
|
| 19 |
-
<p>{{var
|
| 20 |
-
<p>{{depend extraproductcount}} As well as {{var extraproductcount}} more products{{/depend}}.</p>
|
| 21 |
-
<p>Follow this link and log in to finalize your purchase: {{config path="web/unsecure/base_url"}}</p>
|
| 22 |
</div>
|
| 23 |
</td>
|
| 24 |
</tr>
|
| 14 |
<td valign="top">
|
| 15 |
<div>
|
| 16 |
<p>Dear {{var firstname}},</p>
|
| 17 |
+
<p>You have abandoned the following product(s) in your shopping bag:</p>
|
| 18 |
+
{{block type='core/template' area='frontend' template='digitalpianism/abandonedcarts/email/items.phtml' productname=$productname productimage=$productimage}}
|
| 19 |
+
<p>Follow this link and log in to finalize your purchase: <a href="{{var link}}">{{var link}}</a></p>
|
|
|
|
|
|
|
| 20 |
</div>
|
| 21 |
</td>
|
| 22 |
</tr>
|
app/locale/en_US/template/email/digitalpianism/abandonedcarts/sales_abandonedcarts_sale.html
CHANGED
|
@@ -14,16 +14,10 @@
|
|
| 14 |
<td valign="top">
|
| 15 |
<div>
|
| 16 |
<p>Dear {{var firstname}},</p>
|
| 17 |
-
<p>You have abandoned the following product in your shopping bag:</p>
|
| 18 |
-
|
| 19 |
-
<p>{{var
|
| 20 |
-
|
| 21 |
-
<p>
|
| 22 |
-
{{depend extraproductcount}}
|
| 23 |
-
Purchase the {{var extraproductcount}} other sale products in your cart and save {{var discount}} on your order.
|
| 24 |
-
{{/depend}}
|
| 25 |
-
</p>
|
| 26 |
-
<p>Follow this link and log in to finalize your purchase with the new special price: {{config path="web/unsecure/base_url"}}</p>
|
| 27 |
</div>
|
| 28 |
</td>
|
| 29 |
</tr>
|
| 14 |
<td valign="top">
|
| 15 |
<div>
|
| 16 |
<p>Dear {{var firstname}},</p>
|
| 17 |
+
<p>You have abandoned the following product(s) in your shopping bag and they are now on sale:</p>
|
| 18 |
+
{{block type='core/template' area='frontend' template='digitalpianism/abandonedcarts/email/sale_items.phtml' productname=$productname productimage=$productimage cartprice=$cartprice specialprice=$specialprice}}
|
| 19 |
+
<p>Purchase every product in your cart and save {{var discount}} on your order.</p>
|
| 20 |
+
<p>Follow this link to finalize your purchase with the new special price: <a href="{{var link}}">{{var link}}</a></p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
</div>
|
| 22 |
</td>
|
| 23 |
</tr>
|
app/locale/fr_FR/DigitalPianism_Abandonedcarts.csv
CHANGED
|
@@ -3,15 +3,43 @@ Abandoned Carts Emails,Emails des paniers abandonnés
|
|
| 3 |
Enable Abandoned Carts Notification,Activer les notifications de paniers abandonnés
|
| 4 |
Enable Sale Abandoned Carts Notification,Activer les notifications de paniers abandonnés en promo
|
| 5 |
Dry Run,Test
|
| 6 |
-
Setting this parameter to Yes will log all the email addresses supposed to receive a notification into the var/log/digitalpianism_abandonedcarts.log file and will not send the real email notification,Mettre la valeur à Oui enregistrera toutes les adresses emails supposées recevoir une notification dans le fichier var/log/digitalpianism_abandonedcarts.log et n'enverra pas les emails
|
| 7 |
Test Email,Email de test
|
| 8 |
-
With dry run set to yes, this email is used to filter the emails supposed to be sent and only send a notification email to the customer with this email address,Avec Test à Oui, cette adresse email sera utilisée pour filter les emails supposés être envoyé et seulement envoyé l'email de notification au client avec cette adresse
|
| 9 |
Send Notifications Now,Envoyer les notification maintenant
|
| 10 |
Cron Schedule,Réglage du cron
|
| 11 |
Sender Name,Nom de l'expéditeur
|
| 12 |
Sender Email,Email de l'expéditeur
|
| 13 |
Email Template for Unaltered Abandoned Carts,Gabarit d'email pour les paniers abandonnés non modifiés
|
| 14 |
-
|
| 15 |
-
(days). NB: this only affects unaltered abandoned carts. If products go on sale, the email below is sent on the same day.,(jours). NB: cela affecte seulement les paniers abandonnés non modifiés. Si un des produits passent en promotion, l'email ci dessous sera envoyé le m�me jour.
|
| 16 |
Email Template for Sale Products,Gabarit d'email pour les produits en promo
|
| 17 |
-
Customer Groups Restriction,Restriction de groupes clients
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
Enable Abandoned Carts Notification,Activer les notifications de paniers abandonnés
|
| 4 |
Enable Sale Abandoned Carts Notification,Activer les notifications de paniers abandonnés en promo
|
| 5 |
Dry Run,Test
|
|
|
|
| 6 |
Test Email,Email de test
|
|
|
|
| 7 |
Send Notifications Now,Envoyer les notification maintenant
|
| 8 |
Cron Schedule,Réglage du cron
|
| 9 |
Sender Name,Nom de l'expéditeur
|
| 10 |
Sender Email,Email de l'expéditeur
|
| 11 |
Email Template for Unaltered Abandoned Carts,Gabarit d'email pour les paniers abandonnés non modifiés
|
| 12 |
+
(days). NB: this only affects unaltered abandoned carts. If products go on sale, the email below is sent on the same day.,(jours). NB: cela affecte seulement les paniers abandonnés non modifiés. Si un des produits passent en promotion, l'email ci dessous sera envoyé le m�me jour.
|
|
|
|
| 13 |
Email Template for Sale Products,Gabarit d'email pour les produits en promo
|
| 14 |
+
Customer Groups Restriction,Restriction de groupes clients
|
| 15 |
+
With dry run set to yes, this email is used as a recipient email replacement so the real customer don't receive the email.,Avec Test à Oui, cet email sera utilisé en remplacement de l'email du client afin qu'il ne recoive pas l'email
|
| 16 |
+
Setting this parameter to Yes will not send the real email notification.,Si oui, les emails ne seront pas envoyés
|
| 17 |
+
Delay / Send Abandoned Cart Email After,Delai / Envoyer les emails de notifications aprés
|
| 18 |
+
Abandoned Carts (Applied delay: %s days),Paniers Abandonnés (Delai appliqué: %s jours)
|
| 19 |
+
Send notifications,Envoyer les notifications
|
| 20 |
+
Abandoned Carts Logs,Logs de paniers abandonnés
|
| 21 |
+
Sale Abandoned Carts,Paniers abandonnés en promotion
|
| 22 |
+
Customer Email,Email client
|
| 23 |
+
Customer Firstname,Prénom client
|
| 24 |
+
Customer Lastname,Nom de famille client
|
| 25 |
+
Product Ids,Ids Produits
|
| 26 |
+
Product Names,Noms Produits
|
| 27 |
+
Cart Total,Total Panier
|
| 28 |
+
Cart Updated At,Mise à jour panier
|
| 29 |
+
Send notification,Envoyer notification
|
| 30 |
+
Type,Type
|
| 31 |
+
Comment,Commentaire
|
| 32 |
+
Store #,Boutique #
|
| 33 |
+
Cart Regular Total,Total panier
|
| 34 |
+
Cart Sale Total,Total panier en promotion
|
| 35 |
+
%sTotal of %d customer(s) were successfully notified,%s%d client(s) ont bien été notifié
|
| 36 |
+
Logs,Logs
|
| 37 |
+
Abandoned Carts List,Liste des paniers abandonnés
|
| 38 |
+
Abandoned Carts Sale List,Liste des paniers abandonnés en promotion
|
| 39 |
+
Enable Automatic login link in the email,Activer le lien de connexion automatique dans l'email
|
| 40 |
+
Autologin links are one use only and expire after 48 hours for security reasons.,Les liens de connexion automatiques sont à usage unique et expirent au bout de 48 heures pour des raisons de sérité
|
| 41 |
+
Google Campaign Tracker,Tracker Google Campaign
|
| 42 |
+
Campaign Name,Nom de la campagne
|
| 43 |
+
Test and debug,Test et déboguage
|
| 44 |
+
Enable Log,Activer les logs
|
| 45 |
+
Setting this parameter to Yes will log abandoned cart actions so it can be viewed in Digital Pianism > Abandoned Carts > Logs,Si oui, les logs seront visualisables dans Digital Pianism > Paniers Abandonnés > Logs
|
app/locale/fr_FR/template/email/digitalpianism/abandonedcarts/sales_abandonedcarts.html
CHANGED
|
@@ -12,12 +12,10 @@
|
|
| 12 |
<!-- [ middle starts here] -->
|
| 13 |
<tr>
|
| 14 |
<td valign="top">
|
| 15 |
-
<p
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
Suivez ce lien et connectez-vous pour finaliser votre achat: {{config path="web/unsecure/base_url"}}
|
| 20 |
-
</p>
|
| 21 |
</td>
|
| 22 |
</tr>
|
| 23 |
</table>
|
| 12 |
<!-- [ middle starts here] -->
|
| 13 |
<tr>
|
| 14 |
<td valign="top">
|
| 15 |
+
<p>Chèr(e) {{var firstname}},</p>
|
| 16 |
+
<p>Vous avez abandonné le(s) produit(s) suivant(s) dans votre panier.</p>
|
| 17 |
+
{{block type='core/template' area='frontend' template='digitalpianism/abandonedcarts/email/items.phtml' productname=$productname productimage=$productimage}}
|
| 18 |
+
<p>Suivez ce lien et connectez-vous pour finaliser votre achat: <a href="{{var link}}">{{var link}}</a></p>
|
|
|
|
|
|
|
| 19 |
</td>
|
| 20 |
</tr>
|
| 21 |
</table>
|
app/locale/fr_FR/template/email/digitalpianism/abandonedcarts/sales_abandonedcarts_sale.html
CHANGED
|
@@ -12,18 +12,11 @@
|
|
| 12 |
<!-- [ middle starts here] -->
|
| 13 |
<tr>
|
| 14 |
<td valign="top">
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
{{depend extraproductcount}}
|
| 22 |
-
Achetez les {{var extraproductcount}} autre produits en promotion dans votre panier et économisez {{var discount}} sur votre commande.<br/>
|
| 23 |
-
{{/depend}}
|
| 24 |
-
|
| 25 |
-
Suivez ce lien et connectez vous pour finaliser votre achat en profitant des promotions: {{config path="web/unsecure/base_url"}}
|
| 26 |
-
</p>
|
| 27 |
</td>
|
| 28 |
</tr>
|
| 29 |
</table>
|
| 12 |
<!-- [ middle starts here] -->
|
| 13 |
<tr>
|
| 14 |
<td valign="top">
|
| 15 |
+
<p>Chèr(e) {{var firstname}},</p>
|
| 16 |
+
<p>Vous avez abandonné le(s) produit(s) suivant(s) dans votre panier et ils sont désormais en promotion.</p>
|
| 17 |
+
{{block type='core/template' area='frontend' template='digitalpianism/abandonedcarts/email/sale_items.phtml' productname=$productname productimage=$productimage cartprice=$cartprice specialprice=$specialprice}}
|
| 18 |
+
<p>Acheter les produits dans votre panier et économisez {{var discount}} sur votre commande.</p>
|
| 19 |
+
<p>Suivez ce lien et connectez vous pour finaliser votre achat en profitant des promotions: <a href="{{var link}}">{{var link}}</a></p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
</td>
|
| 21 |
</tr>
|
| 22 |
</table>
|
package.xml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
<?xml version="1.0"?>
|
| 2 |
<package>
|
| 3 |
<name>DigitalPianism_Abandonedcarts</name>
|
| 4 |
-
<version>0.
|
| 5 |
<stability>stable</stability>
|
| 6 |
<license uri="http://opensource.org/licenses/osl-3.0.php">OSL v3.0</license>
|
| 7 |
<channel>community</channel>
|
|
@@ -95,11 +95,18 @@ Save the configuration.
|
|
| 95 |

|
| 96 |
<p>To manually trigger the notification system, please access System &gt; Configuration &gt; Digital Pianism &gt; Abandoned carts email and click on the "Send" button</p>
|
| 97 |
<p>Please note that this functionality will send abandoned carts notification regardless the delay you provided, all possible abandoned carts emails will be sent.</p></description>
|
| 98 |
-
<notes>-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
<authors><author><name>Digital Pianism</name><user>digitalpianism</user><email>contact@digital-pianism.com</email></author></authors>
|
| 100 |
-
<date>2016-
|
| 101 |
-
<time>
|
| 102 |
-
<contents><target name="magecommunity"><dir name="DigitalPianism"><dir name="Abandonedcarts"><dir name="Block"><dir name="Adminhtml"><dir name="
|
| 103 |
<compatible/>
|
| 104 |
<dependencies><required><php><min>4.1.0</min><max>6.0.0</max></php></required></dependencies>
|
| 105 |
</package>
|
| 1 |
<?xml version="1.0"?>
|
| 2 |
<package>
|
| 3 |
<name>DigitalPianism_Abandonedcarts</name>
|
| 4 |
+
<version>1.0.0</version>
|
| 5 |
<stability>stable</stability>
|
| 6 |
<license uri="http://opensource.org/licenses/osl-3.0.php">OSL v3.0</license>
|
| 7 |
<channel>community</channel>
|
| 95 |

|
| 96 |
<p>To manually trigger the notification system, please access System &gt; Configuration &gt; Digital Pianism &gt; Abandoned carts email and click on the "Send" button</p>
|
| 97 |
<p>Please note that this functionality will send abandoned carts notification regardless the delay you provided, all possible abandoned carts emails will be sent.</p></description>
|
| 98 |
+
<notes>- Full refactor of the module
|
| 99 |
+
- Add two grids to the backend to see the abandoned carts
|
| 100 |
+
- Add a log database table to easily see what's going on
|
| 101 |
+
- Implement an autologin link system
|
| 102 |
+
- Implement Google Campaign tags
|
| 103 |
+
- Improve the templates to list all items
|
| 104 |
+
- Change the way dryrun and test email behaves
|
| 105 |
+
- Add notification flags columns to the native abandoned carts report</notes>
|
| 106 |
<authors><author><name>Digital Pianism</name><user>digitalpianism</user><email>contact@digital-pianism.com</email></author></authors>
|
| 107 |
+
<date>2016-07-14</date>
|
| 108 |
+
<time>15:18:45</time>
|
| 109 |
+
<contents><target name="magecommunity"><dir name="DigitalPianism"><dir name="Abandonedcarts"><dir name="Block"><dir name="Adminhtml"><dir name="Abandonedcarts"><file name="Grid.php" hash="f0d080d29b7d784004ef412ed5a337d6"/></dir><file name="Abandonedcarts.php" hash="3c8d3c5676cb3bda46a580566f35fd9d"/><dir name="Logs"><file name="Grid.php" hash="05c4ca332a6ad168e28e9a9128252231"/></dir><file name="Logs.php" hash="1173ec175c365fa5c01cbc72a98c0284"/><dir name="Saleabandonedcarts"><file name="Grid.php" hash="cc43378214837f8f9c7c7a66dc47adf4"/></dir><file name="Saleabandonedcarts.php" hash="da29b0f8262da3ec0b993816c5fc5be0"/></dir></dir><dir name="Helper"><file name="Data.php" hash="f7d07930e3276bb06e6209293f287dc3"/></dir><dir name="Model"><dir name="Adminhtml"><file name="Observer.php" hash="d54b2bc70a87fca4f0c3bbb519b84c81"/></dir><file name="Collection.php" hash="3e19497dd2cd2170293136f8cff891dc"/><dir name="Link"><file name="Cleaner.php" hash="aced9e659252056b0f4747a78c6154c8"/></dir><file name="Link.php" hash="6f19c7976980e558d98589021d4d294f"/><file name="Log.php" hash="c9ce940c6a14cfa85c401183559661e4"/><file name="Notifier.php" hash="38c1fcb51994dc155ce62bbb38564526"/><dir name="Resource"><dir name="Link"><file name="Collection.php" hash="39ea2cfb265412d82b9fda822af6d324"/></dir><file name="Link.php" hash="49d00b249de30aefc978f4515f6dbdd7"/><dir name="Log"><file name="Collection.php" hash="54abd79af31a1a853bc08eeed75dc7d0"/></dir><file name="Log.php" hash="00edba4d934093236ac78b42f066ba73"/></dir><dir name="Sales"><dir name="Resource"><file name="Quote.php" hash="3b2f9f24a74a6ea3b6851d64bd6ae5ba"/></dir></dir></dir><dir name="controllers"><dir name="Adminhtml"><file name="AbandonedcartsController.php" hash="32a56a5033b64320879da37f8b2a88e8"/></dir><file name="IndexController.php" hash="5c06db338a20d3de9b19c3f606edbc9a"/></dir><dir name="data"><dir name="abandonedcarts_setup"><file name="data-upgrade-1.0.0-1.0.1.php" hash="a60f9bccf9e42a458f808bc697320bb0"/></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="ce393eb00049f28ff92401be828cd613"/><file name="config.xml" hash="5d3d15cec7d41a670cba49116b01a109"/><file name="system.xml" hash="e6a53269f6223eb246c2495600eb307d"/></dir><dir name="sql"><dir name="abandonedcarts_setup"><file name="install-0.0.1.php" hash="851338e4a710b5d94fead688b065f4b5"/><file name="upgrade-0.0.1-0.0.2.php" hash="0227c009e49b97bcf3f34f84c49f0927"/><file name="upgrade-0.3.6-1.0.0.php" hash="1ac772ef331c8a2278e2c8df77aeb799"/></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="DigitalPianism_Abandonedcarts.xml" hash="8a7657855486c68d548db4ba48e083d2"/></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="template"><dir name="digitalpianism"><dir name="abandonedcarts"><file name="list.phtml" hash="6af16de73f1b0a3c580e65a95642722f"/></dir></dir></dir><dir name="layout"><dir name="digitalpianism"><file name="abandonedcarts.xml" hash="2f4ec5178aed1c84213605b5212d676e"/></dir></dir></dir></dir></dir><dir name="frontend"><dir name="base"><dir name="default"><dir name="template"><dir name="digitalpianism"><dir name="abandonedcarts"><dir name="email"><file name="items.phtml" hash="1938d5ee30752918b1be76be845b9214"/><file name="sale_items.phtml" hash="7dfce25f17ba19532e68592772bf63ad"/></dir></dir></dir></dir></dir></dir></dir></target><target name="magelocale"><dir name="en_US"><dir name="template"><dir name="email"><dir name="digitalpianism"><dir name="abandonedcarts"><file name="sales_abandonedcarts.html" hash="a35bd61e1f172b37ac4ed317e1ad44e9"/><file name="sales_abandonedcarts_sale.html" hash="4f437deca852efeacfec0fb3ba929971"/></dir></dir></dir></dir><file name="DigitalPianism_Abandonedcarts.csv" hash="bd3ed00291684eac5149305ed829a824"/></dir><dir name="fr_FR"><dir name="template"><dir name="email"><dir name="digitalpianism"><dir name="abandonedcarts"><file name="sales_abandonedcarts.html" hash="3ec93757d563ed926090a394577f1dbd"/><file name="sales_abandonedcarts_sale.html" hash="3586968516c8e8374cfa913a3eea7995"/></dir></dir></dir></dir><file name="DigitalPianism_Abandonedcarts.csv" hash="2a9c63b4d83cb922b3060a4735dabe38"/></dir></target></contents>
|
| 110 |
<compatible/>
|
| 111 |
<dependencies><required><php><min>4.1.0</min><max>6.0.0</max></php></required></dependencies>
|
| 112 |
</package>
|
