Version Notes
The Volo extension to Magento, powered by Netmatter, allows all high performance Magento stores – from single instance up to multiple, multi-lingual stores – to carry out 2-way bulk data transfers with the Volo platform in real time, with no loss of Magento performance.
Download this release
Release Info
Developer | Netmatter |
Extension | Netmatter_Bridge |
Version | 1.1.0 |
Comparing to | |
See all releases |
Version 1.1.0
- app/code/community/Netmatter/Bridge/Block/Adminhtml/Config.php +14 -0
- app/code/community/Netmatter/Bridge/Helper/Data.php +2662 -0
- app/code/community/Netmatter/Bridge/Model/Order/Observer.php +75 -0
- app/code/community/Netmatter/Bridge/controllers/CallbackController.php +649 -0
- app/code/community/Netmatter/Bridge/etc/adminhtml.xml +23 -0
- app/code/community/Netmatter/Bridge/etc/config.xml +65 -0
- app/code/community/Netmatter/Bridge/etc/system.xml +65 -0
- app/code/community/Netmatter/Bridge/plugin/bridge.php +2 -0
- app/code/community/Netmatter/Bridge/plugin/config.php +11 -0
- app/etc/modules/Netmatter_Bridge.xml +19 -0
- package.xml +30 -0
- var/bridge/log/warning.txt +1 -0
- var/bridge/queue/warning.txt +1 -0
app/code/community/Netmatter/Bridge/Block/Adminhtml/Config.php
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Netmatter_Bridge_Block_Adminhtml_Config extends Mage_Core_Block_Template
|
4 |
+
{
|
5 |
+
/**
|
6 |
+
* Retrieves the integration's configuration
|
7 |
+
*/
|
8 |
+
public function getConfig()
|
9 |
+
{
|
10 |
+
require ('./app/code/local/Netmatter/Bridge/plugin/bridge.php');
|
11 |
+
|
12 |
+
return $netmatter_bridge_config;
|
13 |
+
}
|
14 |
+
}
|
app/code/community/Netmatter/Bridge/Helper/Data.php
ADDED
@@ -0,0 +1,2662 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @class Netmatter_Bridge_Helper_Data
|
5 |
+
*/
|
6 |
+
class Netmatter_Bridge_Helper_Data extends Mage_Core_Helper_Abstract
|
7 |
+
{
|
8 |
+
private $db = array(); ///< Instances to the db
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Retrieves the table prefix
|
12 |
+
*
|
13 |
+
* @return string
|
14 |
+
*/
|
15 |
+
public function getTablePrefix()
|
16 |
+
{
|
17 |
+
return Mage::getConfig()->getTablePrefix();
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Retrieves the integration version
|
22 |
+
*
|
23 |
+
* @return string the integration version
|
24 |
+
*/
|
25 |
+
public function getIntegrationVersion()
|
26 |
+
{
|
27 |
+
return '1.6.5';
|
28 |
+
}
|
29 |
+
|
30 |
+
public function isEnabled()
|
31 |
+
{
|
32 |
+
return Mage::getStoreConfig('netmatter/settings/enabled',Mage::app()->getStore()) === '1';
|
33 |
+
}
|
34 |
+
|
35 |
+
public function getConfigClientName()
|
36 |
+
{
|
37 |
+
return Mage::getStoreConfig('netmatter/settings/client_name',Mage::app()->getStore());
|
38 |
+
}
|
39 |
+
|
40 |
+
public function getConfigSource()
|
41 |
+
{
|
42 |
+
return Mage::getStoreConfig('netmatter/settings/source',Mage::app()->getStore());
|
43 |
+
}
|
44 |
+
|
45 |
+
public function getConfigAuthenticationToken()
|
46 |
+
{
|
47 |
+
return Mage::getStoreConfig('netmatter/settings/authentication_token',Mage::app()->getStore());
|
48 |
+
}
|
49 |
+
|
50 |
+
public function initBridge()
|
51 |
+
{
|
52 |
+
require (Mage::getModuleDir('', 'Netmatter_Bridge') . '/plugin/bridge.php');
|
53 |
+
|
54 |
+
//Set version details
|
55 |
+
$bridge->setIntegrationVersion($this->getIntegrationVersion());
|
56 |
+
$bridge->setHostVersion($this->getHostVersion());
|
57 |
+
|
58 |
+
$bridge->setCredentials(
|
59 |
+
$this->getConfigClientName(),
|
60 |
+
$this->getConfigSource(),
|
61 |
+
$this->getConfigAuthenticationToken()
|
62 |
+
);
|
63 |
+
|
64 |
+
return $bridge;
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Checks if current installation is single website or not
|
69 |
+
*
|
70 |
+
* @param array $websites the array with the websites
|
71 |
+
* @return boolean
|
72 |
+
*/
|
73 |
+
public function isSingleWebsite($websites)
|
74 |
+
{
|
75 |
+
if (isset($websites[0]))
|
76 |
+
{
|
77 |
+
unset($websites[0]);
|
78 |
+
}
|
79 |
+
|
80 |
+
return count($websites) === 1;
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Retrieves a list with the ids of the websites
|
85 |
+
*
|
86 |
+
* @return array array($website_id => $first_store_id)
|
87 |
+
*/
|
88 |
+
public function getWebsites()
|
89 |
+
{
|
90 |
+
$websites = array();
|
91 |
+
|
92 |
+
$result = $this->db()->query("SELECT MIN(store_id) AS store_id, website_id FROM " . $this->getTablePrefix() . "core_store GROUP BY website_id;");
|
93 |
+
while ($row = $result->fetch(PDO::FETCH_ASSOC))
|
94 |
+
$websites[$row['website_id']] = $row['store_id'];
|
95 |
+
|
96 |
+
return $websites;
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Retrieves a the website id for the given store_id
|
101 |
+
*
|
102 |
+
* @param int $store_id the id of the store
|
103 |
+
* @return int the website_id
|
104 |
+
*/
|
105 |
+
public function getWebsiteIdByStoreId($store_id)
|
106 |
+
{
|
107 |
+
$result = $this->db()->query("SELECT website_id FROM " . $this->getTablePrefix() . "core_store WHERE store_id = :store_id;", array('store_id' => $store_id));
|
108 |
+
return ($row = $result->fetch(PDO::FETCH_ASSOC)) !== false;
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Retrieves a list with the ids of the stores
|
113 |
+
*
|
114 |
+
* @return array containing the stores ids
|
115 |
+
*/
|
116 |
+
public function getStores()
|
117 |
+
{
|
118 |
+
$stores = array();
|
119 |
+
|
120 |
+
$result = $this->db()->query("SELECT store_id, code FROM " . $this->getTablePrefix() . "core_store;");
|
121 |
+
while ($row = $result->fetch(PDO::FETCH_ASSOC))
|
122 |
+
$stores[$row['code']] = $row['store_id'];
|
123 |
+
|
124 |
+
return $stores;
|
125 |
+
}
|
126 |
+
|
127 |
+
/*
|
128 |
+
* Retrieves the "default" tax class for the products
|
129 |
+
*
|
130 |
+
* @return array
|
131 |
+
*/
|
132 |
+
public function getProductDefaultTaxClass()
|
133 |
+
{
|
134 |
+
return Mage::getModel('tax/class')
|
135 |
+
->getCollection()
|
136 |
+
->addFieldToFilter('class_type', 'PRODUCT')
|
137 |
+
->getFirstItem()->getData();
|
138 |
+
}
|
139 |
+
|
140 |
+
public function getProductTaxClassByName($name)
|
141 |
+
{
|
142 |
+
$tax_class = Mage::getModel('tax/class')
|
143 |
+
->getCollection()
|
144 |
+
->addFieldToFilter('class_type', 'PRODUCT')
|
145 |
+
->addFieldToFilter('class_name', $name)
|
146 |
+
->getFirstItem();
|
147 |
+
|
148 |
+
if ($tax_class === null)
|
149 |
+
{
|
150 |
+
return null;
|
151 |
+
}
|
152 |
+
|
153 |
+
return $tax_class->getData();
|
154 |
+
}
|
155 |
+
|
156 |
+
public function getAttributeSetAttributes($attribute_set_id)
|
157 |
+
{
|
158 |
+
$ret = array();
|
159 |
+
$result = $this->db()->query("SELECT attribute_id FROM " . $this->getTablePrefix() . "eav_entity_attribute WHERE attribute_set_id = :attribute_set_id", array('attribute_set_id' => $attribute_set_id));
|
160 |
+
while ($row = $result->fetch(PDO::FETCH_ASSOC))
|
161 |
+
$ret[$row['attribute_id']] = 1;
|
162 |
+
|
163 |
+
return $ret;
|
164 |
+
}
|
165 |
+
|
166 |
+
public function syncProductAttributeSet($mg_product, $attribute_set_name)
|
167 |
+
{
|
168 |
+
$attribute_set_id = $this->getAttributeSetIdByName($attribute_set_name, true); //Create if id doesnt exist
|
169 |
+
$mg_product->setAttributeSetId($attribute_set_id);
|
170 |
+
}
|
171 |
+
|
172 |
+
public function syncProductAttributes($mg_product, $attributes, $attribute_types)
|
173 |
+
{
|
174 |
+
//Read available product attributes
|
175 |
+
$available_attributes = $this->getProductAttributes();
|
176 |
+
|
177 |
+
//Read all attributes for that attribute_set_id and add any needed
|
178 |
+
$attribute_set_id = $mg_product->getAttributeSetId();
|
179 |
+
$set_attributes = $this->getAttributeSetAttributes($attribute_set_id);
|
180 |
+
|
181 |
+
//Create non existing attributes
|
182 |
+
foreach ($attributes AS $attribute_key => $attribute_value)
|
183 |
+
{
|
184 |
+
list($attribute_name, $attribute_code, $store_id) = $this->parseAttributeName($attribute_key);
|
185 |
+
$string_attribute_value = (string)$attribute_value;
|
186 |
+
|
187 |
+
//Check if product has already this value configured and its correct
|
188 |
+
if (!isset($available_attributes[$attribute_code]))
|
189 |
+
{
|
190 |
+
$new_attribute_data = array(
|
191 |
+
'frontend_label' => $attribute_name
|
192 |
+
);
|
193 |
+
|
194 |
+
//Check if we have specific attribute format
|
195 |
+
if (isset($attribute_types[$attribute_key]))
|
196 |
+
{
|
197 |
+
if ($attribute_types[$attribute_key] === 'option')
|
198 |
+
{
|
199 |
+
$new_attribute_data['backend_type'] = 'int';
|
200 |
+
$new_attribute_data['frontend_input'] = 'select';
|
201 |
+
$new_attribute_data['is_global'] = '1';
|
202 |
+
$new_attribute_data['is_configurable'] = '1';
|
203 |
+
$new_attribute_data['apply_to'] = array('simple');
|
204 |
+
}
|
205 |
+
elseif (in_array($attribute_types[$attribute_key], array('identity', 'physical')))
|
206 |
+
{
|
207 |
+
$new_attribute_data['backend_type'] = 'varchar';
|
208 |
+
$new_attribute_data['frontend_input'] = 'text';
|
209 |
+
$new_attribute_data['is_global'] = '1';
|
210 |
+
}
|
211 |
+
}
|
212 |
+
else
|
213 |
+
{
|
214 |
+
$new_attribute_data['backend_type'] = 'varchar';
|
215 |
+
$new_attribute_data['frontend_input'] = 'text';
|
216 |
+
}
|
217 |
+
|
218 |
+
$new_attribute_id = $this->createProductAttribute($attribute_code, $new_attribute_data);
|
219 |
+
|
220 |
+
$available_attributes[$attribute_code] = array(
|
221 |
+
'id' => $new_attribute_id,
|
222 |
+
'frontend_input' => $new_attribute_data['frontend_input'],
|
223 |
+
'backend_type' => $new_attribute_data['backend_type']
|
224 |
+
);
|
225 |
+
}
|
226 |
+
|
227 |
+
$attribute_backend_type = $available_attributes[$attribute_code]['backend_type'];
|
228 |
+
$attribute_frontend_input = $available_attributes[$attribute_code]['frontend_input'];
|
229 |
+
|
230 |
+
//If required add new attribute to set
|
231 |
+
if (!isset($set_attributes[$attribute_code]))
|
232 |
+
{
|
233 |
+
$setup = new Mage_Eav_Model_Entity_Setup('core_setup');
|
234 |
+
$attribute_group_id = $setup->getAttributeGroupId(Mage_Catalog_Model_Product::ENTITY, $attribute_set_id, 'General');
|
235 |
+
$attribute_id = $setup->getAttributeId('catalog_product', $attribute_code);
|
236 |
+
$setup->addAttributeToSet('catalog_product', $attribute_set_id, $attribute_group_id, $attribute_id, 0);
|
237 |
+
}
|
238 |
+
|
239 |
+
//Check attribute type and set the correct value
|
240 |
+
if ($attribute_backend_type === 'int' && $attribute_frontend_input === 'select')
|
241 |
+
{
|
242 |
+
if (isset($string_attribute_value{0}))
|
243 |
+
{
|
244 |
+
$attribute_option = $this->getCreateAttributeOption($available_attributes[$attribute_code]['id'], $string_attribute_value);
|
245 |
+
$this->setProductAttribute($mg_product, $attribute_code, $attribute_option['option_id'], $store_id);
|
246 |
+
}
|
247 |
+
else //Unset empty values
|
248 |
+
{
|
249 |
+
$this->setProductAttribute($mg_product, $attribute_code, null, $store_id);
|
250 |
+
}
|
251 |
+
}
|
252 |
+
elseif ($attribute_backend_type === 'int' && $attribute_frontend_input === 'boolean')
|
253 |
+
{
|
254 |
+
if (!isset($string_attribute_value{0}) || (intval($attribute_value) < 1 && !in_array(strtolower($attribute_value), array('t', 'y', 'true', 'yes', 'on'))))
|
255 |
+
{
|
256 |
+
$this->setProductAttribute($mg_product, $attribute_code, 0, $store_id);
|
257 |
+
}
|
258 |
+
else
|
259 |
+
{
|
260 |
+
$this->setProductAttribute($mg_product, $attribute_code, 1, $store_id);
|
261 |
+
}
|
262 |
+
}
|
263 |
+
elseif (in_array($attribute_backend_type, array('varchar', 'text')))
|
264 |
+
{
|
265 |
+
$this->setProductAttribute($mg_product, $attribute_code, $attribute_value, $store_id);
|
266 |
+
}
|
267 |
+
elseif ($attribute_backend_type === 'int')
|
268 |
+
{
|
269 |
+
$this->setProductAttribute($mg_product, $attribute_code, intval($attribute_value), $store_id);
|
270 |
+
}
|
271 |
+
elseif (in_array($attribute_backend_type, array('float', 'decimal')))
|
272 |
+
{
|
273 |
+
$this->setProductAttribute($mg_product, $attribute_code, number_format($attribute_value, 4), $store_id);
|
274 |
+
}
|
275 |
+
}
|
276 |
+
}
|
277 |
+
|
278 |
+
/**
|
279 |
+
* Returns the destination label, code and store-view_id
|
280 |
+
*
|
281 |
+
* @param string $attribute_key
|
282 |
+
* @return array($attr_name, $attr_code, $scope)
|
283 |
+
*/
|
284 |
+
private function parseAttributeName($attribute_key)
|
285 |
+
{
|
286 |
+
// identify per-site stuff
|
287 |
+
if (preg_match('/^(.*[^-])-(\d+)$/', $attribute_key, $matches))
|
288 |
+
{
|
289 |
+
// TODO: only use live store?
|
290 |
+
$attr_label = str_replace('--', '-', $matches[1]);
|
291 |
+
$store_id = $matches[2];
|
292 |
+
}
|
293 |
+
else
|
294 |
+
{
|
295 |
+
$attr_label = str_replace('--', '-', $attribute_key);
|
296 |
+
$store_id = null;
|
297 |
+
}
|
298 |
+
|
299 |
+
// standardise underscores/camel-casing to spaces
|
300 |
+
$attr_label = preg_replace(array('/_/', '/([A-Z])/'), array(' ', ' \\1'), trim($attr_label));
|
301 |
+
// remove extra whitespace
|
302 |
+
$attr_label = ucwords(trim(preg_replace('/\s+/', ' ', $attr_label)));
|
303 |
+
|
304 |
+
$attr_code = preg_replace('/\s+/', '_', strtolower($attr_label));
|
305 |
+
|
306 |
+
return array($attr_label, $attr_code, $store_id);
|
307 |
+
}
|
308 |
+
|
309 |
+
/**
|
310 |
+
* Sets default attributes to $mg_product, or directly sets per-store ones
|
311 |
+
*
|
312 |
+
* @param Mage_Catalog_Model_Product $mg_product
|
313 |
+
* @param string $attribute_code
|
314 |
+
* @param mixed $attribute_value
|
315 |
+
* @param int|null $store_id
|
316 |
+
*/
|
317 |
+
private function setProductAttribute($mg_product, $attribute_code, $attribute_value, $store_id)
|
318 |
+
{
|
319 |
+
if (is_null($store_id))
|
320 |
+
{
|
321 |
+
$mg_product->setData($attribute_code, $attribute_value);
|
322 |
+
}
|
323 |
+
else
|
324 |
+
{
|
325 |
+
$p = Mage::getModel('catalog/product');
|
326 |
+
$p->setStoreId($store_id);
|
327 |
+
$p->setId($mg_product->getId());
|
328 |
+
$p->setData($attribute_code, $attribute_value);
|
329 |
+
$p->getResource()->saveAttribute($p, $attribute_code);
|
330 |
+
}
|
331 |
+
}
|
332 |
+
|
333 |
+
/**
|
334 |
+
* Retrieves the current website root category id
|
335 |
+
*
|
336 |
+
* @param string $store the store to use
|
337 |
+
* @return int
|
338 |
+
*/
|
339 |
+
public function getRootCategoryId($store = 'default')
|
340 |
+
{
|
341 |
+
return Mage::app()->getStore($store)->getRootCategoryId();
|
342 |
+
}
|
343 |
+
|
344 |
+
public function getTopRootCategoryId()
|
345 |
+
{
|
346 |
+
$category_select = Mage::getModel('catalog/category')
|
347 |
+
->getCollection()
|
348 |
+
->addAttributeToFilter('parent_id', 0)
|
349 |
+
->addAttributeToFilter('attribute_set_id', 0)
|
350 |
+
->addAttributeToFilter('position', 0)
|
351 |
+
->addAttributeToFilter('level', 0)
|
352 |
+
->getSelect();
|
353 |
+
|
354 |
+
$result = $this->db()->query($category_select);
|
355 |
+
if ($row = $result->fetch(PDO::FETCH_ASSOC))
|
356 |
+
return $row['entity_id'];
|
357 |
+
|
358 |
+
throw new Exception('Could not get top root category id');
|
359 |
+
}
|
360 |
+
|
361 |
+
public function getFileContents($url)
|
362 |
+
{
|
363 |
+
//Init curl
|
364 |
+
$curl = curl_init();
|
365 |
+
curl_setopt($curl, CURLOPT_URL, str_replace(' ', '%20', $url));
|
366 |
+
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
367 |
+
curl_setopt($curl, CURLOPT_HEADER, false);
|
368 |
+
|
369 |
+
//Read contents
|
370 |
+
$contents = curl_exec($curl);
|
371 |
+
|
372 |
+
//Close curl
|
373 |
+
curl_close($curl);
|
374 |
+
|
375 |
+
return $contents;
|
376 |
+
}
|
377 |
+
|
378 |
+
public function getTempDirectory()
|
379 |
+
{
|
380 |
+
$tmp_dir = Mage::getBaseDir('tmp');
|
381 |
+
|
382 |
+
while (true)
|
383 |
+
{
|
384 |
+
$dirname = uniqid((string)time(), true);
|
385 |
+
if (!file_exists($tmp_dir . DS . $dirname))
|
386 |
+
{
|
387 |
+
break;
|
388 |
+
}
|
389 |
+
}
|
390 |
+
|
391 |
+
return $tmp_dir . DS . $dirname;
|
392 |
+
}
|
393 |
+
|
394 |
+
public function syncProductImages($mg_product, $images)
|
395 |
+
{
|
396 |
+
//The supported images mime types
|
397 |
+
$mime_types = array(
|
398 |
+
'image/jpeg' => 'jpg',
|
399 |
+
'image/gif' => 'gif',
|
400 |
+
'image/png' => 'png'
|
401 |
+
);
|
402 |
+
|
403 |
+
$mg_product->getResource()->getAttribute('media_gallery')->getBackend()->afterLoad($mg_product);
|
404 |
+
$media_gallery_images = $mg_product->getMediaGalleryImages();
|
405 |
+
|
406 |
+
//Calculate existing images md5 hash
|
407 |
+
$existing_images = array();
|
408 |
+
foreach ($media_gallery_images AS $img)
|
409 |
+
{
|
410 |
+
$existing_images[md5_file($img['path'])] = (array)$img->getData();
|
411 |
+
}
|
412 |
+
|
413 |
+
$media_changes = array(
|
414 |
+
'create' => array(),
|
415 |
+
'update' => array()
|
416 |
+
);
|
417 |
+
|
418 |
+
$i = -1;
|
419 |
+
|
420 |
+
$main_image_position = null;
|
421 |
+
|
422 |
+
//Loop though images and check
|
423 |
+
foreach ($images AS $image)
|
424 |
+
{
|
425 |
+
$i++;
|
426 |
+
|
427 |
+
//Check if label has been set
|
428 |
+
if (!isset($image['label']{0}))
|
429 |
+
{
|
430 |
+
$image['label'] = '';
|
431 |
+
}
|
432 |
+
|
433 |
+
if (isset($image['contents']) && !isset($image['md5'])) //We can calculcate the md5
|
434 |
+
{
|
435 |
+
$image['md5'] = md5($image['contents']);
|
436 |
+
}
|
437 |
+
|
438 |
+
if ($main_image_position === null || (isset($image['is_main_image']) && $image['is_main_image'] === true))
|
439 |
+
{
|
440 |
+
$main_image_position = (string)$i;
|
441 |
+
}
|
442 |
+
|
443 |
+
//Check if md5 hash has been defined. If check all current images for matching
|
444 |
+
if (isset($image['md5']))
|
445 |
+
{
|
446 |
+
if (isset($existing_images[$image['md5']]))
|
447 |
+
{
|
448 |
+
$media_changes['update'][$existing_images[$image['md5']]['id']] = array(
|
449 |
+
'label' => $image['label'],
|
450 |
+
'position' => $i
|
451 |
+
);
|
452 |
+
continue;
|
453 |
+
}
|
454 |
+
}
|
455 |
+
|
456 |
+
//We can load images either using a url or reading directly the contents from the message
|
457 |
+
if (isset($image['contents'])) //From contents
|
458 |
+
{
|
459 |
+
$image_contents = @base64_decode((string)$image['contents'], true);
|
460 |
+
}
|
461 |
+
else
|
462 |
+
if (isset($image['url'])) //From url
|
463 |
+
{
|
464 |
+
$image_contents = $this->getFileContents($image['url']);
|
465 |
+
}
|
466 |
+
else //Not a valid source to read image contents
|
467 |
+
{
|
468 |
+
continue; //Contents or url not set, skip to next image
|
469 |
+
}
|
470 |
+
|
471 |
+
$image['md5'] = md5($image_contents);
|
472 |
+
|
473 |
+
//Check if we already have this image under different name
|
474 |
+
if (isset($existing_images[$image['md5']]))
|
475 |
+
{
|
476 |
+
$media_changes['update'][$existing_images[$image['md5']]['id']] = array(
|
477 |
+
'label' => $image['label'],
|
478 |
+
'position' => $i
|
479 |
+
);
|
480 |
+
continue;
|
481 |
+
}
|
482 |
+
|
483 |
+
//Make image filename
|
484 |
+
if (isset($image['filename']))
|
485 |
+
{
|
486 |
+
$image_filename = $image['filename'];
|
487 |
+
}
|
488 |
+
else
|
489 |
+
if (isset($image['url'])) //From url
|
490 |
+
{
|
491 |
+
$image_filename = basename($image['url']);
|
492 |
+
}
|
493 |
+
else //The md5 hash
|
494 |
+
{
|
495 |
+
$image_filename = $image['md5'];
|
496 |
+
}
|
497 |
+
|
498 |
+
//Write contents to a file, just to determine the mime type
|
499 |
+
$tmpfilename = tempnam(Mage::getBaseDir('tmp'), 'prefix');
|
500 |
+
file_put_contents($tmpfilename, $image_contents);
|
501 |
+
|
502 |
+
//Read contents minetype in order to create the filename extension
|
503 |
+
$image_mime_type = mime_content_type($tmpfilename);
|
504 |
+
|
505 |
+
//Remove file
|
506 |
+
unlink($tmpfilename);
|
507 |
+
|
508 |
+
//Check if mime type is valid
|
509 |
+
if (!isset($mime_types[$image_mime_type]))
|
510 |
+
{
|
511 |
+
throw new Exception('Invalid mime type on image: ' . $image['name']);
|
512 |
+
}
|
513 |
+
|
514 |
+
//From mime types, extract extension
|
515 |
+
$image_extension = $mime_types[$image_mime_type];
|
516 |
+
|
517 |
+
//Remove extension from image
|
518 |
+
$image_filename = preg_replace('/\.[^\.]+$/', '', $image_filename) . '.' . $image_extension;
|
519 |
+
|
520 |
+
$media_changes['create'][] = array(
|
521 |
+
'filename' => $image_filename,
|
522 |
+
'contents' => $image_contents,
|
523 |
+
'label' => $image['label'],
|
524 |
+
'position' => $i
|
525 |
+
);
|
526 |
+
}
|
527 |
+
|
528 |
+
//Update existing images
|
529 |
+
$media_gallery = $mg_product->getData('media_gallery');
|
530 |
+
|
531 |
+
foreach ($media_gallery['images'] AS $key => $media_gallery_image)
|
532 |
+
{
|
533 |
+
$image_id = $media_gallery_image['value_id'];
|
534 |
+
if (isset($media_changes['update'][$image_id]))
|
535 |
+
{
|
536 |
+
$media_gallery['images'][$key]['label'] = $media_changes['update'][$image_id]['label'];
|
537 |
+
$media_gallery['images'][$key]['position'] = $media_changes['update'][$image_id]['position'];
|
538 |
+
$mg_product->setData('media_gallery', $media_gallery);
|
539 |
+
}
|
540 |
+
|
541 |
+
if ($media_gallery_image['position'] === $main_image_position)
|
542 |
+
{
|
543 |
+
$mg_product->setImage($media_gallery_image['file']);
|
544 |
+
$mg_product->setSmallImage($media_gallery_image['file']);
|
545 |
+
$mg_product->setThumbnail($media_gallery_image['file']);
|
546 |
+
}
|
547 |
+
}
|
548 |
+
|
549 |
+
//Create new images
|
550 |
+
foreach ($media_changes['create'] AS $image_to_create)
|
551 |
+
{
|
552 |
+
//We must create a new tmp file with the correct extension, so adding to image gallery will work
|
553 |
+
$tmpdirectory = $this->getTempDirectory();
|
554 |
+
mkdir($tmpdirectory);
|
555 |
+
$tmpfilename = $tmpdirectory . DS . $image_to_create['filename'];
|
556 |
+
@file_put_contents($tmpfilename, $image_to_create['contents']);
|
557 |
+
|
558 |
+
//Add the image to the media gallery of the product
|
559 |
+
$mg_product->addImageToMediaGallery($tmpfilename, array(), true, false);
|
560 |
+
|
561 |
+
//Remove temp directory
|
562 |
+
rmdir($tmpdirectory, true);
|
563 |
+
|
564 |
+
//Add extra attrs
|
565 |
+
$media_gallery = $mg_product->getData('media_gallery');
|
566 |
+
$media_gallery_last_index = count($media_gallery['images']) - 1;
|
567 |
+
|
568 |
+
$media_gallery['images'][$media_gallery_last_index]['label'] = $image_to_create['label'];
|
569 |
+
$media_gallery['images'][$media_gallery_last_index]['position'] = $image_to_create['position'];
|
570 |
+
$mg_product->setData('media_gallery', $media_gallery);
|
571 |
+
}
|
572 |
+
}
|
573 |
+
|
574 |
+
/**
|
575 |
+
* Shortcut function to add a category
|
576 |
+
*
|
577 |
+
* @param array $data the new category's data
|
578 |
+
* @return category
|
579 |
+
*/
|
580 |
+
public function createCategory(array $data)
|
581 |
+
{
|
582 |
+
if (!isset($data['parent_id']))
|
583 |
+
{
|
584 |
+
$data['parent_id'] = $this->getTopRootCategoryId();
|
585 |
+
}
|
586 |
+
|
587 |
+
$category = Mage::getModel('catalog/category');
|
588 |
+
$category->setName($data['name']);
|
589 |
+
$category->setIsActive(1);
|
590 |
+
$category->setDisplayMode('PRODUCTS');
|
591 |
+
$category->setAttributeSetId(Mage::getModel('eav/entity')->setType('catalog_category')->getTypeId());
|
592 |
+
$category->setIsAnchor(isset($data['anchor']) ? $data['anchor'] : 1);
|
593 |
+
$category->setParentId($data['parent_id']);
|
594 |
+
$parentCategory = Mage::getModel('catalog/category')->load($data['parent_id']);
|
595 |
+
|
596 |
+
$category->setPath($parentCategory->getPath());
|
597 |
+
|
598 |
+
foreach (array('meta_title', 'description', 'meta_keywords', 'meta_description') AS $attr)
|
599 |
+
{
|
600 |
+
if (isset($data[$attr]))
|
601 |
+
{
|
602 |
+
$category->setData($attr, $data[$attr]);
|
603 |
+
}
|
604 |
+
}
|
605 |
+
|
606 |
+
if (isset($data['url_key']))
|
607 |
+
{
|
608 |
+
$category->setUrlKey($data['url_key']);
|
609 |
+
}
|
610 |
+
|
611 |
+
if (isset($data['redirect']))
|
612 |
+
{
|
613 |
+
$category->setRedirect($data['redirect']);
|
614 |
+
}
|
615 |
+
|
616 |
+
$category->save();
|
617 |
+
|
618 |
+
return $category;
|
619 |
+
}
|
620 |
+
|
621 |
+
public function getStoresRootCategoryIds()
|
622 |
+
{
|
623 |
+
$ret = array();
|
624 |
+
|
625 |
+
$result = $this->db()->query("SELECT root_category_id, website_id FROM " . $this->getTablePrefix() . "core_store_group WHERE website_id <> 0;");
|
626 |
+
while ($row = $result->fetch(PDO::FETCH_ASSOC))
|
627 |
+
{
|
628 |
+
$ret[$row['website_id']] = $row['root_category_id'];
|
629 |
+
}
|
630 |
+
|
631 |
+
return $ret;
|
632 |
+
}
|
633 |
+
|
634 |
+
/**
|
635 |
+
* Syncs the categoreis of the given product
|
636 |
+
*/
|
637 |
+
public function syncProductCategories($mg_product, $category_data)
|
638 |
+
{
|
639 |
+
//Read current product category ids and their path
|
640 |
+
$product_categories_select = $mg_product->getCategoryCollection()
|
641 |
+
->getSelect()
|
642 |
+
->reset(Zend_Db_Select::COLUMNS)
|
643 |
+
->columns(array('e.entity_id', 'e.path'));
|
644 |
+
|
645 |
+
$current_product_categories = array();
|
646 |
+
$result = $this->db()->query($product_categories_select);
|
647 |
+
while ($row = $result->fetch(PDO::FETCH_ASSOC))
|
648 |
+
{
|
649 |
+
$current_product_categories[$row['entity_id']] = $row['path'];
|
650 |
+
}
|
651 |
+
|
652 |
+
//For all stores get root category ids
|
653 |
+
$stores_root_category_ids = $this->getStoresRootCategoryIds();
|
654 |
+
|
655 |
+
$splitter_character = ' > ';
|
656 |
+
|
657 |
+
//Loop through category fullnames and extract all category names
|
658 |
+
$category_names = array();
|
659 |
+
|
660 |
+
foreach ($category_data AS $store_id => $store_categories)
|
661 |
+
{
|
662 |
+
foreach ($store_categories AS $store_category => $store_category_data)
|
663 |
+
{
|
664 |
+
$fullname_parts = preg_split('/' . $splitter_character . '/', $store_category);
|
665 |
+
foreach ($fullname_parts AS $fullname_part)
|
666 |
+
{
|
667 |
+
$category_names[$fullname_part] = 1;
|
668 |
+
}
|
669 |
+
}
|
670 |
+
}
|
671 |
+
|
672 |
+
//Read categories from db for the names
|
673 |
+
$category_select = Mage::getModel('catalog/category')
|
674 |
+
->setStoreId(0)
|
675 |
+
->getCollection()
|
676 |
+
->addAttributeToFilter('name', array('in' => array_keys($category_names)))
|
677 |
+
->getSelect()
|
678 |
+
->reset(Zend_Db_Select::COLUMNS)
|
679 |
+
->columns(array('e.entity_id', 'e.parent_id', 'at_name.value'));
|
680 |
+
|
681 |
+
$db_categories = array();
|
682 |
+
|
683 |
+
$result = $this->db()->query($category_select);
|
684 |
+
while ($row = $result->fetch(PDO::FETCH_ASSOC))
|
685 |
+
{
|
686 |
+
$db_categories[strtolower($row['value'])][$row['parent_id']] = $row['entity_id'];
|
687 |
+
}
|
688 |
+
|
689 |
+
$new_product_categories = array();
|
690 |
+
$root_categories_ids = array();
|
691 |
+
|
692 |
+
$top_category_id = $this->getTopRootCategoryId();
|
693 |
+
|
694 |
+
foreach ($stores_root_category_ids AS $store_id => $store_root_category_id)
|
695 |
+
{
|
696 |
+
if (isset($category_data[$store_id]))
|
697 |
+
{
|
698 |
+
$store_categories = $category_data[$store_id];
|
699 |
+
}
|
700 |
+
else
|
701 |
+
if (isset($category_data["0"]))
|
702 |
+
{
|
703 |
+
$store_categories = $category_data["0"];
|
704 |
+
}
|
705 |
+
else
|
706 |
+
{
|
707 |
+
continue; //Skip to next store
|
708 |
+
}
|
709 |
+
|
710 |
+
$new_product_categories[] = $store_root_category_id; //Auto append store category id
|
711 |
+
|
712 |
+
foreach ($store_categories AS $store_category_fullname => $store_category_data)
|
713 |
+
{
|
714 |
+
$fullname_parts = preg_split('/' . $splitter_character . '/', $store_category_fullname);
|
715 |
+
$row_category_id = $store_root_category_id;
|
716 |
+
$i = 0;
|
717 |
+
foreach ($fullname_parts AS $category_name)
|
718 |
+
{
|
719 |
+
$category_name_lower = strtolower($category_name);
|
720 |
+
|
721 |
+
$parent_id = $row_category_id;
|
722 |
+
if (isset($db_categories[$category_name_lower][$parent_id])) //Category already exist
|
723 |
+
{
|
724 |
+
$row_category_id = $db_categories[$category_name_lower][$parent_id];
|
725 |
+
}
|
726 |
+
else //Create new category
|
727 |
+
{
|
728 |
+
$new_category = $this->createCategory(array(
|
729 |
+
'name' => $category_name,
|
730 |
+
'parent_id' => $parent_id
|
731 |
+
));
|
732 |
+
$row_category_id = $new_category->getId();
|
733 |
+
$db_categories[$category_name_lower][$parent_id] = $row_category_id;
|
734 |
+
}
|
735 |
+
|
736 |
+
if ($i === 0)
|
737 |
+
{
|
738 |
+
$root_categories_ids[$row_category_id] = 1;
|
739 |
+
}
|
740 |
+
|
741 |
+
$i++;
|
742 |
+
}
|
743 |
+
|
744 |
+
//Add cateogry to product
|
745 |
+
$new_product_categories[] = $row_category_id;
|
746 |
+
}
|
747 |
+
}
|
748 |
+
|
749 |
+
//We need to remove all children categories that belong to root categories that were processed
|
750 |
+
foreach ($stores_root_category_ids AS $store_id => $store_root_category_id)
|
751 |
+
{
|
752 |
+
foreach ($current_product_categories AS $current_category_id => $current_category_path)
|
753 |
+
{
|
754 |
+
if (preg_match('/^' . $top_category_id . '\/' . $store_root_category_id . '(\/|$)/', $current_category_path) > 0)
|
755 |
+
{
|
756 |
+
unset($current_product_categories[$current_category_id]);
|
757 |
+
}
|
758 |
+
}
|
759 |
+
}
|
760 |
+
|
761 |
+
//Create new product_categories array
|
762 |
+
$new_product_categories = array_merge(array_keys($current_product_categories), $new_product_categories);
|
763 |
+
|
764 |
+
//Check if old_categories and new_categories are different
|
765 |
+
$old_product_categories = $mg_product->getCategoryIds();
|
766 |
+
sort($old_product_categories);
|
767 |
+
sort($new_product_categories);
|
768 |
+
|
769 |
+
if ($old_product_categories !== $new_product_categories)
|
770 |
+
{
|
771 |
+
$mg_product->setCategoryIds($new_product_categories);
|
772 |
+
}
|
773 |
+
}
|
774 |
+
|
775 |
+
/**
|
776 |
+
* Sets the status of the given order
|
777 |
+
*
|
778 |
+
* @param $order the magento order object
|
779 |
+
* @param $status the order's new status
|
780 |
+
*/
|
781 |
+
public function setOrderStatus($order, $status)
|
782 |
+
{
|
783 |
+
//Check current order status. Do not continue if it is the same
|
784 |
+
if ($order->getStatus() === $status)
|
785 |
+
{
|
786 |
+
return;
|
787 |
+
}
|
788 |
+
|
789 |
+
//Find state based on status
|
790 |
+
$status_details = Mage::getModel('sales/order_status')
|
791 |
+
->getCollection()
|
792 |
+
->joinStates()
|
793 |
+
->addFieldToFilter('main_table.status', $status)
|
794 |
+
->getFirstItem();
|
795 |
+
|
796 |
+
//Update order's status. Magento does have a separate function for completed or closed
|
797 |
+
switch ($status_details['state'])
|
798 |
+
{
|
799 |
+
//Order is cancelled
|
800 |
+
case 'cancelled':
|
801 |
+
if (!$order->canCancel())
|
802 |
+
{
|
803 |
+
throw new Exception('Order cannot be cancelled');
|
804 |
+
}
|
805 |
+
|
806 |
+
$order->cancel();
|
807 |
+
|
808 |
+
break;
|
809 |
+
|
810 |
+
//Order is complete
|
811 |
+
case 'complete':
|
812 |
+
|
813 |
+
//Disable order observer. Wrong order status is sent to bridge when payment is not captured yet
|
814 |
+
Mage::register('netmatter_bridge_disable_order_observer', 1);
|
815 |
+
|
816 |
+
//Check if order can be invoiced
|
817 |
+
if ($order->canInvoice())
|
818 |
+
{
|
819 |
+
//Create invoice
|
820 |
+
$invoice = Mage::getModel('sales/service_order', $order)->prepareInvoice();
|
821 |
+
|
822 |
+
$invoice->setRequestedCaptureCase(Mage_Sales_Model_Order_Invoice::CAPTURE_ONLINE);
|
823 |
+
$invoice->register();
|
824 |
+
|
825 |
+
//Save
|
826 |
+
$transaction = Mage::getModel('core/resource_transaction')
|
827 |
+
->addObject($invoice)
|
828 |
+
->addObject($invoice->getOrder());
|
829 |
+
$transaction->save();
|
830 |
+
}
|
831 |
+
|
832 |
+
//Set again state/status manually
|
833 |
+
$order->setData('state', 'complete');
|
834 |
+
$order->setStatus($status);
|
835 |
+
$history = $order->addStatusHistoryComment('', false);
|
836 |
+
$history->setIsCustomerNotified(true);
|
837 |
+
|
838 |
+
//Re-enable
|
839 |
+
Mage::register('netmatter_disable_order_observer', 0);
|
840 |
+
|
841 |
+
//Trigger order to be resend
|
842 |
+
Mage::helper('bridge/data')->sendOrder($order);
|
843 |
+
|
844 |
+
break;
|
845 |
+
|
846 |
+
//Order is closed. Not supported [Closed orders are orders that have had a credit memo assigned to it and the customer has been refunded for their order]
|
847 |
+
case 'closed':
|
848 |
+
|
849 |
+
break;
|
850 |
+
|
851 |
+
//Any other status
|
852 |
+
default:
|
853 |
+
|
854 |
+
$order->setState($status_details['state'], $status);
|
855 |
+
|
856 |
+
break;
|
857 |
+
}
|
858 |
+
|
859 |
+
//Save order
|
860 |
+
$order->save();
|
861 |
+
}
|
862 |
+
|
863 |
+
/**
|
864 |
+
* Calculates a hash from the given shipment details
|
865 |
+
*
|
866 |
+
* @param array $shipment the shipment to use
|
867 |
+
* @return string
|
868 |
+
*/
|
869 |
+
public function calculateShipmentHash(array $shipment)
|
870 |
+
{
|
871 |
+
$x = array(
|
872 |
+
'rows' => $shipment['rows'],
|
873 |
+
'shippedOn' => $shipment['shippedOn']
|
874 |
+
);
|
875 |
+
|
876 |
+
return sha1(serialize($x));
|
877 |
+
}
|
878 |
+
|
879 |
+
/**
|
880 |
+
* Retrieves a shipment by the order and hash
|
881 |
+
*
|
882 |
+
* @param Order $order the order to use
|
883 |
+
* @param string $hash the hash code to search
|
884 |
+
* @return Shipment|null
|
885 |
+
*/
|
886 |
+
public function getShipmentByOrderHash($order, $hash)
|
887 |
+
{
|
888 |
+
$shipments = $order->getShipmentsCollection();
|
889 |
+
foreach ($shipments AS $shipment)
|
890 |
+
{
|
891 |
+
$shash = $this->getShipmentHash($shipment);
|
892 |
+
if ($shash === $hash)
|
893 |
+
{
|
894 |
+
return $shipment;
|
895 |
+
}
|
896 |
+
}
|
897 |
+
|
898 |
+
return null;
|
899 |
+
}
|
900 |
+
|
901 |
+
/**
|
902 |
+
* Retrieves the shipment's hash for the given shipment
|
903 |
+
*
|
904 |
+
* @param Shipment $shipment the shipment to use
|
905 |
+
* @return string|null
|
906 |
+
*/
|
907 |
+
public function getShipmentHash($shipment)
|
908 |
+
{
|
909 |
+
$prefix = 'Netmatter Hash: ';
|
910 |
+
|
911 |
+
//Hash is stored within shipment's comments
|
912 |
+
foreach ($shipment->getCommentsCollection() AS $comment)
|
913 |
+
{
|
914 |
+
$comment = $comment->getData('comment');
|
915 |
+
preg_match('/^Netmatter Hash: ([a-zA-Z0-9]+)$/', $comment, $matches);
|
916 |
+
if (count($matches) === 0)
|
917 |
+
{
|
918 |
+
return null;
|
919 |
+
}
|
920 |
+
|
921 |
+
return $matches[1];
|
922 |
+
}
|
923 |
+
|
924 |
+
return null;
|
925 |
+
}
|
926 |
+
|
927 |
+
/**
|
928 |
+
* Syncs the shipments for the given order
|
929 |
+
*
|
930 |
+
* @param Order $order the order to sync the shipments
|
931 |
+
* @param array $shipments the shipments data
|
932 |
+
*/
|
933 |
+
public function syncShipments($order, array $shipments)
|
934 |
+
{
|
935 |
+
//Check if we can create shipments for the given order
|
936 |
+
if (!$order->canShip())
|
937 |
+
{
|
938 |
+
return; //We cannot modify shipments
|
939 |
+
}
|
940 |
+
|
941 |
+
//Read shipments for the given order
|
942 |
+
$mg_shipment_collection = Mage::getResourceModel('sales/order_shipment_collection');
|
943 |
+
$mg_shipment_collection->addAttributeToFilter('order_id', $order->getId());
|
944 |
+
|
945 |
+
//Retrieve order products
|
946 |
+
$magento_order_items = array();
|
947 |
+
foreach ($order->getAllItems() AS $item)
|
948 |
+
{
|
949 |
+
// we need to ignore non-shipping rows so they don't overwrite the correct item_id
|
950 |
+
if ($item->getQtyToShip() > 0)
|
951 |
+
{
|
952 |
+
$magento_order_items[$item['sku']] = $item['item_id'];
|
953 |
+
}
|
954 |
+
}
|
955 |
+
|
956 |
+
//Check if all request products exist
|
957 |
+
foreach ($shipments AS $key => $shipment)
|
958 |
+
{
|
959 |
+
foreach ($shipment['rows'] AS $row)
|
960 |
+
{
|
961 |
+
if (!isset($magento_order_items[$row['sku']]))
|
962 |
+
{
|
963 |
+
//At least one product not found. Cannot continue
|
964 |
+
unset($shipments[$key]);
|
965 |
+
break;
|
966 |
+
}
|
967 |
+
}
|
968 |
+
}
|
969 |
+
|
970 |
+
//Sync shipments
|
971 |
+
foreach ($shipments AS $shipment)
|
972 |
+
{
|
973 |
+
//Check if shipment already exists (Hash checking)
|
974 |
+
$shipment_hash = $this->calculateShipmentHash($shipment);
|
975 |
+
if ($this->getShipmentByOrderHash($order, $shipment_hash) !== null)
|
976 |
+
{
|
977 |
+
continue;
|
978 |
+
}
|
979 |
+
|
980 |
+
//Make qty array
|
981 |
+
$qty = array();
|
982 |
+
foreach ($shipment['rows'] AS $row)
|
983 |
+
{
|
984 |
+
$qty[$magento_order_items[$row['sku']]] = $row['quantity'];
|
985 |
+
}
|
986 |
+
|
987 |
+
if (empty($qty)) //Could not found any products to ship
|
988 |
+
{
|
989 |
+
continue;
|
990 |
+
}
|
991 |
+
|
992 |
+
//Create new shipment
|
993 |
+
$new_shipment = Mage::getModel('sales/service_order', $order)->prepareShipment($qty);
|
994 |
+
if ($new_shipment)
|
995 |
+
{
|
996 |
+
$new_shipment->addComment('Netmatter Hash: ' . $shipment_hash);
|
997 |
+
|
998 |
+
$new_shipment->register();
|
999 |
+
$new_shipment->getOrder()->setIsInProcess(true);
|
1000 |
+
$transaction = Mage::getModel('core/resource_transaction')
|
1001 |
+
->addObject($new_shipment)
|
1002 |
+
->addObject($new_shipment->getOrder());
|
1003 |
+
$transaction->save();
|
1004 |
+
}
|
1005 |
+
}
|
1006 |
+
}
|
1007 |
+
|
1008 |
+
/**
|
1009 |
+
* Sets the tracking codes for the given order
|
1010 |
+
*
|
1011 |
+
* @param $order the magento order object
|
1012 |
+
* @param array $tracking_codes an array containing the tracking codes
|
1013 |
+
* @param array $shipment_hashes an array containing the shipment hases
|
1014 |
+
*/
|
1015 |
+
public function setOrderTrackingCodes($order, array $tracking_codes, array $shipment_hashes = null)
|
1016 |
+
{
|
1017 |
+
//Read shipments for the given order
|
1018 |
+
$shipment_collection = Mage::getResourceModel('sales/order_shipment_collection');
|
1019 |
+
$shipment_collection->addAttributeToFilter('order_id', $order->getId());
|
1020 |
+
|
1021 |
+
//Append the tracking codes to all shipments, if they exist
|
1022 |
+
foreach($shipment_collection AS $mg_shipment)
|
1023 |
+
{
|
1024 |
+
//Extract tracking codes from shipment
|
1025 |
+
$mg_tracking_codes = array();
|
1026 |
+
foreach ($mg_shipment->getAllTracks() AS $tracknum)
|
1027 |
+
{
|
1028 |
+
$mg_tracking_codes[] = $tracknum->getNumber();
|
1029 |
+
}
|
1030 |
+
|
1031 |
+
$mg_shipment_hash = $this->getShipmentHash($mg_shipment);
|
1032 |
+
|
1033 |
+
foreach ($tracking_codes AS $shipping_method => $tracking_code)
|
1034 |
+
{
|
1035 |
+
//Check if tracking code has been already set
|
1036 |
+
if (in_array($tracking_code, $mg_tracking_codes))
|
1037 |
+
{
|
1038 |
+
continue;
|
1039 |
+
}
|
1040 |
+
|
1041 |
+
//Check hash
|
1042 |
+
if (isset($shipment_hashes[$tracking_code]) && isset($mg_shipment_hash{0}) && $shipment_hashes[$tracking_code] !== $mg_shipment_hash)
|
1043 |
+
{
|
1044 |
+
continue;
|
1045 |
+
}
|
1046 |
+
|
1047 |
+
//Add tracking line
|
1048 |
+
$track = Mage::getModel('sales/order_shipment_track')
|
1049 |
+
->setData('title', $shipping_method)
|
1050 |
+
->setData('number', $tracking_code)
|
1051 |
+
->setData('carrier_code', 'custom')
|
1052 |
+
->setData('order_id', $order->getId());
|
1053 |
+
|
1054 |
+
$mg_shipment->addTrack($track);
|
1055 |
+
$mg_shipment->save();
|
1056 |
+
|
1057 |
+
$mg_shipment->sendEmail(true);
|
1058 |
+
}
|
1059 |
+
}
|
1060 |
+
}
|
1061 |
+
|
1062 |
+
/**
|
1063 |
+
* Retrieves a list with the ids of the available groups
|
1064 |
+
*
|
1065 |
+
* @return array containing the groups
|
1066 |
+
*/
|
1067 |
+
public function getGroups()
|
1068 |
+
{
|
1069 |
+
$groups = array();
|
1070 |
+
$result = $this->db()->query("SELECT customer_group_id, customer_group_code FROM " . $this->getTablePrefix() . "customer_group;");
|
1071 |
+
while ($row = $result->fetch(PDO::FETCH_ASSOC))
|
1072 |
+
$groups[$row['customer_group_code']] = $row['customer_group_id'];
|
1073 |
+
|
1074 |
+
return $groups;
|
1075 |
+
}
|
1076 |
+
|
1077 |
+
/**
|
1078 |
+
* Retrieves the attributes that the product's can have
|
1079 |
+
*
|
1080 |
+
* @return an array containing the product attributes
|
1081 |
+
*/
|
1082 |
+
public function getProductAttributes()
|
1083 |
+
{
|
1084 |
+
$attributes = array();
|
1085 |
+
$result = $this->db()->query("SELECT attribute_id, attribute_code, frontend_input, backend_type FROM " . $this->getTablePrefix() . "eav_attribute WHERE entity_type_id = 4;");
|
1086 |
+
while ($row = $result->fetch(PDO::FETCH_ASSOC))
|
1087 |
+
{
|
1088 |
+
$attributes[$row['attribute_code']] = array(
|
1089 |
+
'id' => $row['attribute_id'],
|
1090 |
+
'frontend_input' => $row['frontend_input'],
|
1091 |
+
'backend_type' => $row['backend_type']
|
1092 |
+
);
|
1093 |
+
}
|
1094 |
+
|
1095 |
+
return $attributes;
|
1096 |
+
}
|
1097 |
+
|
1098 |
+
public function getAttributeSets($entity_type_id = null)
|
1099 |
+
{
|
1100 |
+
$ret = array();
|
1101 |
+
$attribute_set_collection = Mage::getModel("eav/entity_attribute_set")->getCollection();
|
1102 |
+
foreach ($attribute_set_collection AS $attribute_set)
|
1103 |
+
{
|
1104 |
+
if ($entity_type_id !== null && (string)$attribute_set->getEntityTypeId() !== (string)$entity_type_id)
|
1105 |
+
{
|
1106 |
+
continue;
|
1107 |
+
}
|
1108 |
+
$ret[$attribute_set->getAttributeSetName()] = $attribute_set->getAttributeSetId();
|
1109 |
+
}
|
1110 |
+
|
1111 |
+
return $ret;
|
1112 |
+
}
|
1113 |
+
|
1114 |
+
/**
|
1115 |
+
* Retrieves the id of the product entity type
|
1116 |
+
*
|
1117 |
+
* @return int
|
1118 |
+
*/
|
1119 |
+
public function getCatalogProductEntityTypeId()
|
1120 |
+
{
|
1121 |
+
return Mage::getModel('catalog/product')->getResource()->getEntityType()->getId();
|
1122 |
+
}
|
1123 |
+
|
1124 |
+
/**
|
1125 |
+
* Retrieves the default attribute set id of the products
|
1126 |
+
*
|
1127 |
+
* @return int
|
1128 |
+
*/
|
1129 |
+
public function getProductDefaultAttributeSetId()
|
1130 |
+
{
|
1131 |
+
return Mage::getModel('catalog/product')->getDefaultAttributeSetId();
|
1132 |
+
}
|
1133 |
+
|
1134 |
+
/**
|
1135 |
+
* Creates an attribute set from the given name
|
1136 |
+
*
|
1137 |
+
* @param string $attribute_set_name the name of the attribute set
|
1138 |
+
* @return int the attribute_set id
|
1139 |
+
*/
|
1140 |
+
public function createAttributeSet($attribute_set_name)
|
1141 |
+
{
|
1142 |
+
return (string)Mage::getModel('catalog/product_attribute_set_api')
|
1143 |
+
->create($attribute_set_name, $this->getProductDefaultAttributeSetId());
|
1144 |
+
}
|
1145 |
+
|
1146 |
+
public function getAttributeSetIdByName($attribute_set_name, $create = null)
|
1147 |
+
{
|
1148 |
+
$attribute_sets = $this->getAttributeSets();
|
1149 |
+
if (isset($attribute_sets[$attribute_set_name]))
|
1150 |
+
{
|
1151 |
+
return $attribute_sets[$attribute_set_name];
|
1152 |
+
}
|
1153 |
+
|
1154 |
+
//Attribute set does not exists, and no create flag isset
|
1155 |
+
if ($create !== true)
|
1156 |
+
{
|
1157 |
+
return null;
|
1158 |
+
}
|
1159 |
+
|
1160 |
+
//Create attribute set
|
1161 |
+
return $this->createAttributeSet($attribute_set_name);
|
1162 |
+
}
|
1163 |
+
|
1164 |
+
public function createProductAttribute($attribute_code, $attribute_data)
|
1165 |
+
{
|
1166 |
+
$default_attribute_data = array(
|
1167 |
+
'attribute_code' => $attribute_code,
|
1168 |
+
'is_global' => '0',
|
1169 |
+
'frontend_input' => 'text',
|
1170 |
+
'backend_type' => 'varchar',
|
1171 |
+
'default_value_yesno' => '0',
|
1172 |
+
'default_value_text' => '',
|
1173 |
+
'default_value_textarea' => '',
|
1174 |
+
'default_value_date' => '',
|
1175 |
+
'is_unique' => '0',
|
1176 |
+
'is_required' => '0',
|
1177 |
+
'apply_to' => array(),
|
1178 |
+
'is_configurable' => '0',
|
1179 |
+
'is_searchable' => '0',
|
1180 |
+
'is_visible_in_advanced_search' => '0',
|
1181 |
+
'is_comparable' => '0',
|
1182 |
+
'is_wysiwyg_enabled' => '0',
|
1183 |
+
'is_used_for_price_rules' => '0',
|
1184 |
+
'is_visible_on_front' => '0',
|
1185 |
+
'is_html_allowed_on_front' => '0',
|
1186 |
+
'used_for_sort_by' => '0',
|
1187 |
+
'used_in_product_listing' => '0',
|
1188 |
+
'frontend_label' => 'New Attribute'
|
1189 |
+
);
|
1190 |
+
|
1191 |
+
$attribute_data = array_merge($default_attribute_data, $attribute_data);
|
1192 |
+
|
1193 |
+
$model = Mage::getModel('catalog/resource_eav_attribute');
|
1194 |
+
$model->addData($attribute_data);
|
1195 |
+
$model->setEntityTypeId(Mage::getModel('eav/entity')->setType('catalog_product')->getTypeId());
|
1196 |
+
$model->setIsUserDefined(1);
|
1197 |
+
|
1198 |
+
return $model->save()->getId();
|
1199 |
+
}
|
1200 |
+
|
1201 |
+
/**
|
1202 |
+
* Retrieve an array containing the product ids that have the given sku
|
1203 |
+
*
|
1204 |
+
* @param $product_sku the sku to use
|
1205 |
+
* @return an array contanining the ids
|
1206 |
+
*/
|
1207 |
+
public function getProductIdsBySku($product_sku)
|
1208 |
+
{
|
1209 |
+
$ids = array();
|
1210 |
+
$result = $this->db()->query("SELECT entity_id FROM " . $this->getTablePrefix() . "catalog_product_entity WHERE sku = :sku;", array('sku' => $product_sku));
|
1211 |
+
while ($row = $result->fetch(PDO::FETCH_ASSOC))
|
1212 |
+
$ids[] = $row['entity_id'];
|
1213 |
+
|
1214 |
+
return $ids;
|
1215 |
+
}
|
1216 |
+
|
1217 |
+
/**
|
1218 |
+
* Retrieve an array containing the available options for the given attribute
|
1219 |
+
*
|
1220 |
+
* @param $attribute_id the id of the attribute
|
1221 |
+
* @param $store_id the id of the store
|
1222 |
+
* @return an array contanining the attribute's options
|
1223 |
+
*/
|
1224 |
+
public function getAttributeOptions($attribute_id, $store_id = null)
|
1225 |
+
{
|
1226 |
+
$stores = array(0);
|
1227 |
+
if ($store_id !== null)
|
1228 |
+
$stores[] = $store_id;
|
1229 |
+
|
1230 |
+
$options = array();
|
1231 |
+
$result = $this->db()->query("SELECT option_id, value FROM " . $this->getTablePrefix() . "eav_attribute_option_value WHERE option_id IN (SELECT option_id FROM " . $this->getTablePrefix() . "eav_attribute_option WHERE attribute_id = :attribute_id) AND store_id IN (" . implode(',', $stores) . ");", array('attribute_id' => $attribute_id));
|
1232 |
+
while ($row = $result->fetch(PDO::FETCH_ASSOC))
|
1233 |
+
$options[$row['value']] = $row['option_id'];
|
1234 |
+
|
1235 |
+
return $options;
|
1236 |
+
}
|
1237 |
+
|
1238 |
+
/**
|
1239 |
+
* Retrieve or creates an attribute option value
|
1240 |
+
*
|
1241 |
+
* @param $attribute_id the id of the attribute
|
1242 |
+
* @param $store_id the id of the store
|
1243 |
+
* @return an array contanining the attribute's options
|
1244 |
+
*/
|
1245 |
+
public function getCreateAttributeOption($attribute_id, $value, $store_id = null)
|
1246 |
+
{
|
1247 |
+
$stores = array(0);
|
1248 |
+
if ($store_id !== null)
|
1249 |
+
$stores[] = $store_id;
|
1250 |
+
|
1251 |
+
$options = array();
|
1252 |
+
$result = $this->db()->query("SELECT option_id, value FROM " . $this->getTablePrefix() . "eav_attribute_option_value WHERE option_id IN (SELECT option_id FROM " . $this->getTablePrefix() . "eav_attribute_option WHERE attribute_id = :attribute_id) AND store_id IN (" . implode(',', $stores) . ") AND value = :value;", array('attribute_id' => $attribute_id, 'value' => $value));
|
1253 |
+
if ($row = $result->fetch(PDO::FETCH_ASSOC))
|
1254 |
+
return $row;
|
1255 |
+
|
1256 |
+
//Create the option
|
1257 |
+
$this->addAttributeOption($attribute_id, $value);
|
1258 |
+
|
1259 |
+
//Recurse call to get the newly created value
|
1260 |
+
return $this->getCreateAttributeOption($attribute_id, $value, $store_id);
|
1261 |
+
}
|
1262 |
+
|
1263 |
+
/**
|
1264 |
+
* Adds an options to an attribute drop down
|
1265 |
+
*
|
1266 |
+
* @param $attribute_id the id of the attribute
|
1267 |
+
* @param $attribute_value the option's value
|
1268 |
+
*/
|
1269 |
+
public function addAttributeOption($attribute_id, $option_value)
|
1270 |
+
{
|
1271 |
+
$option['attribute_id'] = $attribute_id;
|
1272 |
+
$option['value']['0_' . $option_value][0] = $option_value;
|
1273 |
+
|
1274 |
+
$setup = new Mage_Eav_Model_Entity_Setup('core_setup');
|
1275 |
+
$setup->addAttributeOption($option);
|
1276 |
+
}
|
1277 |
+
|
1278 |
+
/**
|
1279 |
+
* Checks if the given value is an integer or not
|
1280 |
+
*
|
1281 |
+
* @param $value the value to check
|
1282 |
+
* @return true if value is an integer, false if not
|
1283 |
+
*/
|
1284 |
+
public function isInteger($value)
|
1285 |
+
{
|
1286 |
+
return (!($type === 'boolean' || filter_var($value, FILTER_VALIDATE_INT) === false));
|
1287 |
+
}
|
1288 |
+
|
1289 |
+
/**
|
1290 |
+
* Retrieve product(s) stock by sku
|
1291 |
+
*
|
1292 |
+
* @param $sku the sku or array or sku
|
1293 |
+
* @return an array containing sku, product_id and stock
|
1294 |
+
*/
|
1295 |
+
public function getProductStockBySku($sku)
|
1296 |
+
{
|
1297 |
+
//Make sure given parameter is array
|
1298 |
+
$sku = (array)$sku;
|
1299 |
+
|
1300 |
+
//Make in query
|
1301 |
+
$in_query = implode(',', array_fill(0, count($sku), '?'));
|
1302 |
+
|
1303 |
+
//Make statement
|
1304 |
+
$stmt = $this->db()->prepare("SELECT sku, entity_id, qty FROM " . $this->getTablePrefix() . "catalog_product_entity LEFT JOIN " . $this->getTablePrefix() . "cataloginventory_stock_item ON product_id = entity_id WHERE sku IN (" . $in_query . ");");
|
1305 |
+
|
1306 |
+
//Bind values
|
1307 |
+
foreach ($sku as $key => $value)
|
1308 |
+
{
|
1309 |
+
$stmt->bindValue($key + 1, $value, PDO::PARAM_STR);
|
1310 |
+
}
|
1311 |
+
|
1312 |
+
//Execute query
|
1313 |
+
$stmt->execute();
|
1314 |
+
|
1315 |
+
//Fetch results
|
1316 |
+
$stock = array();
|
1317 |
+
while ($row = $stmt->fetch(PDO::FETCH_ASSOC))
|
1318 |
+
{
|
1319 |
+
$stock[$row['sku']] = array('product_id' => $row['entity_id'], 'qty' => $row['qty']);
|
1320 |
+
}
|
1321 |
+
|
1322 |
+
return $stock;
|
1323 |
+
}
|
1324 |
+
|
1325 |
+
/**
|
1326 |
+
* Sets the stock level for the given product
|
1327 |
+
*
|
1328 |
+
* @param int $product_id
|
1329 |
+
* @param int $qty The new stock level
|
1330 |
+
*/
|
1331 |
+
public function setProductStockById($product_id, $qty)
|
1332 |
+
{
|
1333 |
+
$multiwarehouse = is_array($qty);
|
1334 |
+
|
1335 |
+
//Multiple stock items
|
1336 |
+
$total_qty = 0;
|
1337 |
+
if (!$multiwarehouse)
|
1338 |
+
{
|
1339 |
+
$total_qty = $qty;
|
1340 |
+
}
|
1341 |
+
|
1342 |
+
//Check for multi warehouse
|
1343 |
+
if ($multiwarehouse && Mage::getModel('advancedinventory/stock')->getMultiStockEnabledByProductId($product_id))
|
1344 |
+
{
|
1345 |
+
foreach ($qty AS $store_id => $value)
|
1346 |
+
{
|
1347 |
+
$advanced_inventory_stocks = Mage::getModel('advancedinventory/stock')->getStocksByProductIdAndStoreId($product_id, $store_id);
|
1348 |
+
|
1349 |
+
foreach ($advanced_inventory_stocks AS $advanced_inventory_stock)
|
1350 |
+
{
|
1351 |
+
$advanced_stock_item = Mage::getModel('advancedinventory/stock')->getStockByProductIdAndPlaceId($product_id, $advanced_inventory_stock->getPlaceId());
|
1352 |
+
if ($advanced_stock_item->getId() === null) //Skip places not found
|
1353 |
+
{
|
1354 |
+
continue;
|
1355 |
+
}
|
1356 |
+
|
1357 |
+
$total_qty += $value;
|
1358 |
+
|
1359 |
+
$advanced_stock_item->setData('quantity_in_stock', $value);
|
1360 |
+
|
1361 |
+
$advanced_stock_item->save();
|
1362 |
+
}
|
1363 |
+
}
|
1364 |
+
}
|
1365 |
+
|
1366 |
+
//Retrieve stock item
|
1367 |
+
$stockItem = Mage::getModel('cataloginventory/stock_item')->loadByProduct($product_id);
|
1368 |
+
if (!$stockItem->getId())
|
1369 |
+
{
|
1370 |
+
$stockItem->setData('product_id', $product_id);
|
1371 |
+
$stockItem->setData('stock_id', $this->getDefaultStockId());
|
1372 |
+
|
1373 |
+
//Save and reload
|
1374 |
+
$stockItem->save();
|
1375 |
+
$stockItem = Mage::getModel('cataloginventory/stock_item')->loadByProduct($product_id);
|
1376 |
+
}
|
1377 |
+
|
1378 |
+
$product_backorders = ($stockItem->getUseConfigBackorders() == 1 && Mage::getStoreConfig('cataloginventory/item_options/backorders') == 1) ||
|
1379 |
+
($stockItem->getUseConfigBackorders() == 0 && $stockItem->getBackorders() > 0);
|
1380 |
+
|
1381 |
+
$new_is_in_stock_flag = ($total_qty > 0 || $product_backorders === true) ? 1 : 0;
|
1382 |
+
|
1383 |
+
//Check if stock needs to be updated
|
1384 |
+
if (netmatter_float_equals((float)$stockItem->getQty(), (float)$total_qty) &&
|
1385 |
+
$stockItem->getManageStock() == 1 &&
|
1386 |
+
$stockItem->getIsInStock() == $new_is_in_stock_flag)
|
1387 |
+
{
|
1388 |
+
return;
|
1389 |
+
}
|
1390 |
+
|
1391 |
+
$stockItem->setData('qty', $total_qty);
|
1392 |
+
$stockItem->setData('manage_stock', 1);
|
1393 |
+
$stockItem->setData('use_config_manage_stock', 0);
|
1394 |
+
$stockItem->setData('is_in_stock', $new_is_in_stock_flag);
|
1395 |
+
$stockItem->save();
|
1396 |
+
}
|
1397 |
+
|
1398 |
+
/**
|
1399 |
+
* Sets the stock level for the given product
|
1400 |
+
*
|
1401 |
+
* @param $product the magento product
|
1402 |
+
* @param $qty the stock level
|
1403 |
+
* @return the product
|
1404 |
+
*/
|
1405 |
+
public function setProductStock($product, $qty)
|
1406 |
+
{
|
1407 |
+
$product->setStockData(array(
|
1408 |
+
'use_config_manage_stock' => 0,
|
1409 |
+
'manage_stock' => 1,
|
1410 |
+
'is_in_stock' => $qty > 0 ? 1 : 0,
|
1411 |
+
'qty' => $qty
|
1412 |
+
)
|
1413 |
+
);
|
1414 |
+
|
1415 |
+
return $product;
|
1416 |
+
}
|
1417 |
+
|
1418 |
+
/**
|
1419 |
+
* Retrieves the default stock id
|
1420 |
+
*
|
1421 |
+
* @return integer the default stock id
|
1422 |
+
*/
|
1423 |
+
public function getDefaultStockId()
|
1424 |
+
{
|
1425 |
+
//Find stock with name "Default"
|
1426 |
+
$result = $this->db()->query("SELECT stock_id FROM " . $this->getTablePrefix() . "cataloginventory_stock WHERE stock_name = 'Default';");
|
1427 |
+
$row = $result->fetch(PDO::FETCH_ASSOC);
|
1428 |
+
if ($row !== false)
|
1429 |
+
{
|
1430 |
+
return $row['stock_id'];
|
1431 |
+
}
|
1432 |
+
|
1433 |
+
//Get the minimum stock id
|
1434 |
+
$result = $this->db()->query("SELECT MIN(stock_id) AS min_stock_id FROM " . $this->getTablePrefix() . "cataloginventory_stock;");
|
1435 |
+
$row = $result->fetch(PDO::FETCH_ASSOC);
|
1436 |
+
|
1437 |
+
return $row !== false ? $row['min_stock_id'] : 1;
|
1438 |
+
}
|
1439 |
+
|
1440 |
+
/**
|
1441 |
+
* Retrieves the product's parent product id
|
1442 |
+
*
|
1443 |
+
* @param $product_id the id of the product
|
1444 |
+
* @return the product's parent id if exists, or null if product does not have a parent product
|
1445 |
+
*/
|
1446 |
+
public function getProductParentId($product_id)
|
1447 |
+
{
|
1448 |
+
$result = $this->db()->query("SELECT parent_id FROM " . $this->getTablePrefix() . "catalog_product_relation WHERE child_id = :child_id;", array('child_id' => $product_id));
|
1449 |
+
$row = $result->fetch(PDO::FETCH_ASSOC);
|
1450 |
+
return $row !== false ? $row['parent_id'] : null;
|
1451 |
+
}
|
1452 |
+
|
1453 |
+
/**
|
1454 |
+
* Checks if a product with the given id exists or not
|
1455 |
+
*
|
1456 |
+
* @param $product_id the id of the product
|
1457 |
+
* @return true or false
|
1458 |
+
*/
|
1459 |
+
public function productExists($product_id)
|
1460 |
+
{
|
1461 |
+
$result = $this->db()->query("SELECT 1 FROM " . $this->getTablePrefix() . "catalog_product_entity WHERE entity_id = :entity_id;", array('entity_id' => $product_id));
|
1462 |
+
return ($row = $result->fetch(PDO::FETCH_ASSOC)) !== false;
|
1463 |
+
}
|
1464 |
+
|
1465 |
+
/**
|
1466 |
+
* Disables a product based on its id
|
1467 |
+
*
|
1468 |
+
* @param $product_id the id of the product
|
1469 |
+
* @param $website_id the id of the website
|
1470 |
+
*/
|
1471 |
+
public function disableProduct($product_id, $website_id = 0)
|
1472 |
+
{
|
1473 |
+
Mage::getModel('catalog/product_status')->updateProductStatus($product_id, $website_id, Mage_Catalog_Model_Product_Status::STATUS_DISABLED);
|
1474 |
+
}
|
1475 |
+
|
1476 |
+
/**
|
1477 |
+
* Retrieves super links that a product has
|
1478 |
+
*
|
1479 |
+
* @param $product_id the id of the product
|
1480 |
+
* @return an array containing the product's super links
|
1481 |
+
*/
|
1482 |
+
public function getProductSuperLinks($product_id)
|
1483 |
+
{
|
1484 |
+
$super_links = array();
|
1485 |
+
$result = $this->db()->query("SELECT link_id, parent_id FROM " . $this->getTablePrefix() . "catalog_product_super_link WHERE product_id = :product_id;", array('product_id' => $product_id));
|
1486 |
+
while ($row = $result->fetch(PDO::FETCH_ASSOC))
|
1487 |
+
$super_links[$row['link_id']] = $row['parent_id'];
|
1488 |
+
|
1489 |
+
return $super_links;
|
1490 |
+
}
|
1491 |
+
|
1492 |
+
/**
|
1493 |
+
* Retrieves the prices array of the given product id by simply quering the database so we can update prices only if price have changed
|
1494 |
+
*
|
1495 |
+
* @param $product_id the id of the product
|
1496 |
+
* @return an array containing the product's prices
|
1497 |
+
*/
|
1498 |
+
public function getSimpleProductPrices($product_id)
|
1499 |
+
{
|
1500 |
+
//Get attributes ids
|
1501 |
+
$attributes = $this->getProductAttributes();
|
1502 |
+
$filter_attributes = array(
|
1503 |
+
$attributes['cost']['id'] => 'cost',
|
1504 |
+
$attributes['msrp']['id'] => 'msrp',
|
1505 |
+
$attributes['price']['id'] => 'price',
|
1506 |
+
$attributes['special_price']['id'] => 'special_price',
|
1507 |
+
$attributes['special_to_date']['id'] => 'special_to_date',
|
1508 |
+
$attributes['special_from_date']['id'] => 'special_from_date'
|
1509 |
+
);
|
1510 |
+
|
1511 |
+
//Make query
|
1512 |
+
$query = "SELECT 'price' AS type, 1 AS qty, attribute_id AS id, store_id AS website_id, value FROM " . $this->getTablePrefix() . "catalog_product_entity_decimal WHERE entity_id = :product_id and attribute_id in (" . implode(', ', array_filter(array_keys($filter_attributes))) . ")
|
1513 |
+
UNION
|
1514 |
+
SELECT 'tier' AS type, FLOOR(qty) AS qty, customer_group_id AS id, website_id, value FROM " . $this->getTablePrefix() . "catalog_product_entity_tier_price WHERE entity_id = :product_id";
|
1515 |
+
|
1516 |
+
if ($this->supportsGroupPrices())
|
1517 |
+
{
|
1518 |
+
$query .= " UNION SELECT 'group' AS type, 1 AS qty, customer_group_id AS id, website_id, value FROM " . $this->getTablePrefix() . "catalog_product_entity_group_price WHERE entity_id = :product_id";
|
1519 |
+
}
|
1520 |
+
|
1521 |
+
//Read prices
|
1522 |
+
$prices = array();
|
1523 |
+
$result = $this->db()->query($query, array('product_id' => $product_id));
|
1524 |
+
while ($row = $result->fetch(PDO::FETCH_ASSOC))
|
1525 |
+
{
|
1526 |
+
if (!isset($prices[$row['website_id']]['group']))
|
1527 |
+
{
|
1528 |
+
$prices[$row['website_id']]['group'] = array();
|
1529 |
+
}
|
1530 |
+
|
1531 |
+
if (!isset($prices[$row['website_id']]['tier']))
|
1532 |
+
{
|
1533 |
+
$prices[$row['website_id']]['tier'] = array();
|
1534 |
+
}
|
1535 |
+
|
1536 |
+
switch ($row['type'])
|
1537 |
+
{
|
1538 |
+
case 'group':
|
1539 |
+
$prices[$row['website_id']]['group'][$row['id']]["1"] = $row['value'];
|
1540 |
+
break;
|
1541 |
+
|
1542 |
+
case 'tier':
|
1543 |
+
if ($row['id'] > 0) //Specific group
|
1544 |
+
{
|
1545 |
+
$prices[$row['website_id']]['group'][$row['id']][$row['qty']] = $row['value'];
|
1546 |
+
}
|
1547 |
+
else //Retail
|
1548 |
+
{
|
1549 |
+
$prices[$row['website_id']]['tier'][$row['qty']] = $row['value'];
|
1550 |
+
}
|
1551 |
+
|
1552 |
+
break;
|
1553 |
+
|
1554 |
+
default:
|
1555 |
+
$prices[$row['website_id']][$filter_attributes[$row['id']]] = $row['value'];
|
1556 |
+
}
|
1557 |
+
}
|
1558 |
+
|
1559 |
+
return $prices;
|
1560 |
+
}
|
1561 |
+
|
1562 |
+
/**
|
1563 |
+
* Retrieves a Magento product by its id for the given store_id
|
1564 |
+
*
|
1565 |
+
* @param $product_id the id of the product
|
1566 |
+
* @param $store_id the id of the store. Leave to null for default
|
1567 |
+
* @return the Magento product
|
1568 |
+
*/
|
1569 |
+
public function getProduct($product_id, $store_id = null)
|
1570 |
+
{
|
1571 |
+
$model = Mage::getModel('catalog/product');
|
1572 |
+
if ($store_id !== null)
|
1573 |
+
{
|
1574 |
+
$model->setStoreId($store_id);
|
1575 |
+
}
|
1576 |
+
|
1577 |
+
$product = $model->getCollection()->addAttributeToSelect('*')->addAttributeToFilter('entity_id', $product_id)->getFirstItem();
|
1578 |
+
if ($product->getId() !== $product_id) //Check for some custom magento
|
1579 |
+
{
|
1580 |
+
$product = $model->load($product_id);
|
1581 |
+
}
|
1582 |
+
|
1583 |
+
return $product;
|
1584 |
+
}
|
1585 |
+
|
1586 |
+
/**
|
1587 |
+
* Returns the sites that pricing is enabled for
|
1588 |
+
*
|
1589 |
+
* @param array $linked_websites array($first_store_id => $website_id)
|
1590 |
+
* @param array $websites array($website_id => $first_store_id)
|
1591 |
+
* @return array($foo => $bar)
|
1592 |
+
*/
|
1593 |
+
public function getPricingWebsites($linked_websites, $websites)
|
1594 |
+
{
|
1595 |
+
$pricing_websites = $linked_websites;
|
1596 |
+
|
1597 |
+
//Check for single website. If it we have to set the 0 website instead of 1
|
1598 |
+
if ($this->isSingleWebsite($websites))
|
1599 |
+
{
|
1600 |
+
$linked_websites_keys = array_keys($linked_websites);
|
1601 |
+
$pricing_websites = array($linked_websites_keys[0] => 0);
|
1602 |
+
}
|
1603 |
+
else
|
1604 |
+
{
|
1605 |
+
//Always set the zero website
|
1606 |
+
$pricing_websites[0] = 0;
|
1607 |
+
}
|
1608 |
+
|
1609 |
+
return array_unique($pricing_websites);
|
1610 |
+
}
|
1611 |
+
|
1612 |
+
/**
|
1613 |
+
* Retrieves the super pricing for the given product
|
1614 |
+
*
|
1615 |
+
* @param $product_id the id of the product
|
1616 |
+
* @return array the super pricing
|
1617 |
+
*/
|
1618 |
+
public function getProductSuperAttributePricing($product_id)
|
1619 |
+
{
|
1620 |
+
$pricing = array();
|
1621 |
+
$result = $this->db()->query("SELECT * FROM " . $this->getTablePrefix() . "catalog_product_super_attribute_pricing p JOIN " . $this->getTablePrefix() . "catalog_product_super_attribute a ON p.product_super_attribute_id = a.product_super_attribute_id WHERE product_id = :product_id;", array('product_id' => $product_id));
|
1622 |
+
while ($row = $result->fetch(PDO::FETCH_ASSOC))
|
1623 |
+
{
|
1624 |
+
$pricing[$row['value_id']] = $row;
|
1625 |
+
}
|
1626 |
+
|
1627 |
+
return $pricing;
|
1628 |
+
}
|
1629 |
+
|
1630 |
+
/**
|
1631 |
+
* Quickly reads the price for the given product for the given store_id
|
1632 |
+
*
|
1633 |
+
* Reads store : 0 if no custom price for the given store exists
|
1634 |
+
*
|
1635 |
+
* @param $product_id the id of the product
|
1636 |
+
* @param $store_id the id of the store
|
1637 |
+
* @return the product's price
|
1638 |
+
*/
|
1639 |
+
public function getProductPrice($product_id, $store_id)
|
1640 |
+
{
|
1641 |
+
$attributes = $this->getProductAttributes();
|
1642 |
+
|
1643 |
+
$values = array(
|
1644 |
+
'store_id' => $store_id,
|
1645 |
+
'entity_id' => $product_id,
|
1646 |
+
'attribute_id' => $attributes['price']['id']
|
1647 |
+
);
|
1648 |
+
|
1649 |
+
$result = $this->db()->query("SELECT value FROM " . $this->getTablePrefix() . "catalog_product_entity_decimal WHERE store_id IN (0, :store_id) AND value IS NOT NULL AND value <> '' AND entity_id = :entity_id AND attribute_id = :attribute_id ORDER BY store_id DESC LIMIT 1;", $values);
|
1650 |
+
$row = $result->fetch(PDO::FETCH_ASSOC);
|
1651 |
+
return $row !== false ? $row['value'] : 0.00;
|
1652 |
+
}
|
1653 |
+
|
1654 |
+
/**
|
1655 |
+
* Retrieves an array containing the int attributes and values for the given entity
|
1656 |
+
*
|
1657 |
+
* @param $entity_id the id of the entity
|
1658 |
+
* @retun array
|
1659 |
+
*/
|
1660 |
+
public function getIntAttributesForEntity($entity_id)
|
1661 |
+
{
|
1662 |
+
$attributes = array();
|
1663 |
+
$result = $this->db()->query("SELECT attribute_id, store_id, (SELECT website_id FROM " . $this->getTablePrefix() . "core_store WHERE store_id = p.store_id) AS website, value FROM " . $this->getTablePrefix() . "catalog_product_entity_int AS p WHERE entity_id = :entity_id;", array('entity_id' => $entity_id));
|
1664 |
+
while ($row = $result->fetch(PDO::FETCH_ASSOC))
|
1665 |
+
{
|
1666 |
+
$attributes[$row['attribute_id']] = $row;
|
1667 |
+
}
|
1668 |
+
|
1669 |
+
return $attributes;
|
1670 |
+
}
|
1671 |
+
|
1672 |
+
/**
|
1673 |
+
* Sets the price for the given product_id on the given product super link id for the given website
|
1674 |
+
*
|
1675 |
+
* @param $product_super_link_id the super link id
|
1676 |
+
* @param $product_id the product's id
|
1677 |
+
* @param $new_price the new product's price
|
1678 |
+
* @param $website_id the id of the website
|
1679 |
+
* @param $store_id the id of the store
|
1680 |
+
*/
|
1681 |
+
public function setSuperLinkPrice($product_super_link_id, $product_id, $new_price, $website_id, $store_id)
|
1682 |
+
{
|
1683 |
+
//Read data
|
1684 |
+
$pricing_list = $this->getProductSuperAttributePricing($product_super_link_id);
|
1685 |
+
$child_attributes = $this->getIntAttributesForEntity($product_id);
|
1686 |
+
|
1687 |
+
$matched_price = null;
|
1688 |
+
|
1689 |
+
//Find which attribute_id and value_index is the correct
|
1690 |
+
foreach ($pricing_list as $pricing)
|
1691 |
+
{
|
1692 |
+
foreach ($child_attributes as $child_attribute)
|
1693 |
+
{
|
1694 |
+
if ($pricing['attribute_id'] == $child_attribute['attribute_id'] && $pricing['value_index'] == $child_attribute['value'])
|
1695 |
+
{
|
1696 |
+
if ($matched_price=== null || $website_id == $pricing['website_id'])
|
1697 |
+
{
|
1698 |
+
$matched_price = $pricing;
|
1699 |
+
break;
|
1700 |
+
}
|
1701 |
+
}
|
1702 |
+
}
|
1703 |
+
}
|
1704 |
+
|
1705 |
+
if ($matched_price === null) //We cannot automatically set. Customer must makes the variation manually first
|
1706 |
+
{
|
1707 |
+
return;
|
1708 |
+
}
|
1709 |
+
|
1710 |
+
//Calculate new price based on conf product's price
|
1711 |
+
$conf_price = $this->getProductPrice($product_super_link_id, $store_id);
|
1712 |
+
$new_price -= $conf_price;
|
1713 |
+
|
1714 |
+
//Check if price needs to be updated
|
1715 |
+
if ($matched_price['website_id'] == $website_id && $matched_price['is_percent'] == 0 && $matched_price['pricing_value'] == $new_price)
|
1716 |
+
{
|
1717 |
+
return; //No need to update
|
1718 |
+
}
|
1719 |
+
|
1720 |
+
//Update pricing
|
1721 |
+
if (isset($matched_price['value_id']{0}) && $matched_price['website_id'] == $website_id)
|
1722 |
+
{
|
1723 |
+
$this->db()->query("UPDATE " . $this->getTablePrefix() . "catalog_product_super_attribute_pricing SET pricing_value = :pricing_value WHERE value_id = :value_id;", array('pricing_value' => $new_price, 'value_id' => $matched_price['value_id']));
|
1724 |
+
}
|
1725 |
+
else //Insert pricing
|
1726 |
+
{
|
1727 |
+
$insert_values = array(
|
1728 |
+
':product_super_attribute_id' => $matched_price['product_super_attribute_id'],
|
1729 |
+
':value_index' => $matched_price['value_index'],
|
1730 |
+
':pricing_value' => $new_price,
|
1731 |
+
':website_id' => $website_id,
|
1732 |
+
);
|
1733 |
+
|
1734 |
+
$this->db()->query("INSERT INTO " . $this->getTablePrefix() . "catalog_product_super_attribute_pricing(product_super_attribute_id, value_index, is_percent, pricing_value, website_id) VALUES (:product_super_attribute_id, :value_index, 0, :pricing_value, :website_id);", $insert_values);
|
1735 |
+
}
|
1736 |
+
}
|
1737 |
+
|
1738 |
+
/**
|
1739 |
+
* Checks if the current version supports group prices or not
|
1740 |
+
*
|
1741 |
+
* @return boolean
|
1742 |
+
*/
|
1743 |
+
public function supportsGroupPrices()
|
1744 |
+
{
|
1745 |
+
return $this->db()->isTableExists($this->getTablePrefix() . 'catalog_product_entity_group_price');
|
1746 |
+
}
|
1747 |
+
|
1748 |
+
/**
|
1749 |
+
* Sets the prices for the given product
|
1750 |
+
*
|
1751 |
+
* @param int $product_id the id of the product
|
1752 |
+
* @param array $prices an array containing the prices
|
1753 |
+
* @param array $websites array($store_id => $website_id)
|
1754 |
+
*/
|
1755 |
+
public function setSimpleProductPrices($product_id, $prices, array $websites)
|
1756 |
+
{
|
1757 |
+
//Retrieve product's current prices
|
1758 |
+
$current_prices = $this->getSimpleProductPrices($product_id);
|
1759 |
+
|
1760 |
+
foreach ($websites AS $store_id => $website_id)
|
1761 |
+
{
|
1762 |
+
$attributes_data = array(
|
1763 |
+
'msrp' => isset($prices['rrp-' . $website_id]['1']) ? $prices['rrp-' . $website_id]['1'] : '',
|
1764 |
+
'cost' => isset($prices['cost-' . $website_id]['1']) ? $prices['cost-' . $website_id]['1'] : '',
|
1765 |
+
);
|
1766 |
+
|
1767 |
+
//Retail price
|
1768 |
+
if (isset($prices['retail-' . $website_id]['1']))
|
1769 |
+
{
|
1770 |
+
$attributes_data['price'] = $prices['retail-' . $website_id]['1'];
|
1771 |
+
}
|
1772 |
+
|
1773 |
+
//Check for sale price
|
1774 |
+
if (!array_key_exists('sale-' . $website_id, $prices))
|
1775 |
+
{
|
1776 |
+
// it's not mapped - don't change anything
|
1777 |
+
}
|
1778 |
+
elseif (isset($prices['sale-' . $website_id]['1']))
|
1779 |
+
{
|
1780 |
+
$attributes_data['special_price'] = $prices['sale-' . $website_id]['1'];
|
1781 |
+
}
|
1782 |
+
elseif (isset($prices['sale-' . $website_id]['price']))
|
1783 |
+
{
|
1784 |
+
$sale_obj = $prices['sale-' . $website_id];
|
1785 |
+
$attributes_data['special_price'] = $sale_obj['price'];
|
1786 |
+
$attributes_data['special_from_date'] = isset($sale_obj['start']) ? $sale_obj['start'] : null;
|
1787 |
+
$attributes_data['special_to_date'] = isset($sale_obj['end']) ? $sale_obj['end'] : null;
|
1788 |
+
}
|
1789 |
+
else //Remove sale price
|
1790 |
+
{
|
1791 |
+
$attributes_data['special_price'] = '';
|
1792 |
+
$attributes_data['special_to_date'] = '';
|
1793 |
+
$attributes_data['special_from_date'] = '';
|
1794 |
+
}
|
1795 |
+
|
1796 |
+
foreach ($attributes_data as $key => $value)
|
1797 |
+
{
|
1798 |
+
if ((isset($current_prices[$website_id][$key]) && $current_prices[$website_id][$key] == $value) || (!isset($current_prices[$website_id][$key]) && $value === ''))
|
1799 |
+
{
|
1800 |
+
unset($attributes_data[$key]);
|
1801 |
+
}
|
1802 |
+
}
|
1803 |
+
|
1804 |
+
//Check if update is required
|
1805 |
+
if (!empty($attributes_data))
|
1806 |
+
{
|
1807 |
+
Mage::getSingleton('catalog/product_action')->updateAttributes(array($product_id), $attributes_data, $store_id);
|
1808 |
+
}
|
1809 |
+
|
1810 |
+
//Check for product super links
|
1811 |
+
$product_super_links = $this->getProductSuperLinks($product_id);
|
1812 |
+
if (count($product_super_links) === 0)
|
1813 |
+
{
|
1814 |
+
continue;
|
1815 |
+
}
|
1816 |
+
|
1817 |
+
//In case that retail price or sale is updated and the product belongs to configurable products, we must properly set the values
|
1818 |
+
$new_price = null;
|
1819 |
+
if (isset($prices['sale-' . $website_id]['1'])) //Use sale over price
|
1820 |
+
{
|
1821 |
+
$new_price = $prices['sale-' . $website_id]['1'];
|
1822 |
+
}
|
1823 |
+
elseif (isset($prices['retail-' . $website_id]['1']))
|
1824 |
+
{
|
1825 |
+
$new_price = $prices['retail-' . $website_id]['1'];
|
1826 |
+
}
|
1827 |
+
|
1828 |
+
if ($new_price === null)
|
1829 |
+
{
|
1830 |
+
continue;
|
1831 |
+
}
|
1832 |
+
|
1833 |
+
//Get product's super links
|
1834 |
+
foreach ($product_super_links as $product_super_link)
|
1835 |
+
{
|
1836 |
+
$this->setSuperLinkPrice($product_super_link, $product_id, $new_price, $website_id, $store_id);
|
1837 |
+
}
|
1838 |
+
}
|
1839 |
+
|
1840 |
+
//Check for group and tier prices
|
1841 |
+
$group_prices = array();
|
1842 |
+
$tier_prices = array();
|
1843 |
+
|
1844 |
+
$groups = $this->getGroups();
|
1845 |
+
|
1846 |
+
foreach ($websites AS $store_id => $website_id)
|
1847 |
+
{
|
1848 |
+
//Check for retail price
|
1849 |
+
if (isset($prices['retail-' . $website_id]['1']))
|
1850 |
+
{
|
1851 |
+
foreach ($prices['retail-' . $website_id] AS $tier_qty => $price)
|
1852 |
+
{
|
1853 |
+
if ($tier_qty == 1)
|
1854 |
+
{
|
1855 |
+
continue;
|
1856 |
+
}
|
1857 |
+
|
1858 |
+
$tier_prices[] = array(
|
1859 |
+
'website_id' => $website_id,
|
1860 |
+
'cust_group' => Mage_Customer_Model_Group::CUST_GROUP_ALL,
|
1861 |
+
'price' => $price,
|
1862 |
+
'price_qty' => $tier_qty
|
1863 |
+
);
|
1864 |
+
}
|
1865 |
+
}
|
1866 |
+
|
1867 |
+
//Check for groups prices
|
1868 |
+
foreach ($groups AS $group_id)
|
1869 |
+
{
|
1870 |
+
if (isset($prices['group_' . $group_id . '-' . $website_id]['1']))
|
1871 |
+
{
|
1872 |
+
foreach ($prices['group_' . $group_id . '-' . $website_id] AS $tier_qty => $price)
|
1873 |
+
{
|
1874 |
+
if ($tier_qty == 1) //Price for 1 qty is stored under group prices
|
1875 |
+
{
|
1876 |
+
$group_prices[] = array(
|
1877 |
+
'website_id' => $website_id,
|
1878 |
+
'cust_group' => $group_id,
|
1879 |
+
'price' => $price,
|
1880 |
+
);
|
1881 |
+
}
|
1882 |
+
else //Prices for more than 1 qty are stored as tier price
|
1883 |
+
{
|
1884 |
+
$tier_prices[] = array(
|
1885 |
+
'website_id' => $website_id,
|
1886 |
+
'cust_group' => $group_id,
|
1887 |
+
'price' => $price,
|
1888 |
+
'price_qty' => $tier_qty
|
1889 |
+
);
|
1890 |
+
}
|
1891 |
+
}
|
1892 |
+
}
|
1893 |
+
}
|
1894 |
+
}
|
1895 |
+
|
1896 |
+
|
1897 |
+
//Create compare arrays
|
1898 |
+
$mg_group_price = array();
|
1899 |
+
$mg_tier_price = array();
|
1900 |
+
foreach ($current_prices as $website_id => $pricelists)
|
1901 |
+
{
|
1902 |
+
foreach ($pricelists['group'] as $group_id => $values)
|
1903 |
+
{
|
1904 |
+
foreach ($values as $qty => $price)
|
1905 |
+
{
|
1906 |
+
if ($qty == 1)
|
1907 |
+
{
|
1908 |
+
$mg_group_price[] = array(
|
1909 |
+
'website_id' => $website_id,
|
1910 |
+
'cust_group' => $group_id,
|
1911 |
+
'price' => $price
|
1912 |
+
);
|
1913 |
+
}
|
1914 |
+
else
|
1915 |
+
{
|
1916 |
+
$mg_tier_price[] = array(
|
1917 |
+
'website_id' => $website_id,
|
1918 |
+
'cust_group' => $group_id,
|
1919 |
+
'price' => $price,
|
1920 |
+
'price_qty' => $qty
|
1921 |
+
);
|
1922 |
+
}
|
1923 |
+
}
|
1924 |
+
}
|
1925 |
+
|
1926 |
+
foreach ($pricelists['tier'] as $qty => $price)
|
1927 |
+
{
|
1928 |
+
$mg_tier_price[] = array(
|
1929 |
+
'website_id' => $website_id,
|
1930 |
+
'cust_group' => Mage_Customer_Model_Group::CUST_GROUP_ALL,
|
1931 |
+
'price' => $price,
|
1932 |
+
'price_qty' => $qty
|
1933 |
+
);
|
1934 |
+
}
|
1935 |
+
}
|
1936 |
+
|
1937 |
+
array_multisort($mg_tier_price);
|
1938 |
+
array_multisort($tier_prices);
|
1939 |
+
array_multisort($mg_group_price);
|
1940 |
+
array_multisort($group_prices);
|
1941 |
+
|
1942 |
+
$tier_prices_different = $mg_tier_price != $tier_prices;
|
1943 |
+
$group_prices_different = $mg_group_price != $group_prices;
|
1944 |
+
|
1945 |
+
// Only update if necessary
|
1946 |
+
if ($tier_prices_different || $group_prices_different)
|
1947 |
+
{
|
1948 |
+
$supportsGroupPrices = $this->supportsGroupPrices();
|
1949 |
+
|
1950 |
+
$mg_product = Mage::getModel('catalog/product')->setStoreId(0)->load($product_id);
|
1951 |
+
|
1952 |
+
$mg_product->unsTierPrice();
|
1953 |
+
if ($supportsGroupPrices === true)
|
1954 |
+
{
|
1955 |
+
$mg_product->unsGroupPrice();
|
1956 |
+
}
|
1957 |
+
|
1958 |
+
//We need to double save to prevent integrity errors. Magento lol. Also in later version we need to fully reload product
|
1959 |
+
$mg_product->save();
|
1960 |
+
$mg_product = Mage::getModel('catalog/product')->setStoreId(0)->load($product_id);
|
1961 |
+
|
1962 |
+
$mg_product->setTierPrice($tier_prices);
|
1963 |
+
if ($supportsGroupPrices === true)
|
1964 |
+
{
|
1965 |
+
$mg_product->setGroupPrice($group_prices);
|
1966 |
+
}
|
1967 |
+
|
1968 |
+
$mg_product->save();
|
1969 |
+
}
|
1970 |
+
}
|
1971 |
+
|
1972 |
+
/**
|
1973 |
+
* Retrieve the instance to the db
|
1974 |
+
*
|
1975 |
+
* @return the db instance
|
1976 |
+
*/
|
1977 |
+
public function db($write = false)
|
1978 |
+
{
|
1979 |
+
$key = $write === true ? 'write' : 'read';
|
1980 |
+
|
1981 |
+
if (!isset($this->db[$key]))
|
1982 |
+
{
|
1983 |
+
$this->db[$key] = Mage::getSingleton('core/resource')->getConnection('core_' . $key);
|
1984 |
+
}
|
1985 |
+
|
1986 |
+
return $this->db[$key];
|
1987 |
+
}
|
1988 |
+
|
1989 |
+
/**
|
1990 |
+
* Retrieves the host's version
|
1991 |
+
*
|
1992 |
+
* @retun string
|
1993 |
+
*/
|
1994 |
+
public function getHostVersion()
|
1995 |
+
{
|
1996 |
+
return 'Magento ' . Mage::getVersion();
|
1997 |
+
}
|
1998 |
+
|
1999 |
+
/**
|
2000 |
+
* Sends an order to the bridge
|
2001 |
+
*
|
2002 |
+
* @param Mage_Sales_Model_Order $magento_order
|
2003 |
+
*/
|
2004 |
+
public function sendOrder($magento_order)
|
2005 |
+
{
|
2006 |
+
/* @var $bridge Netmatter_Bridge_Bridge */
|
2007 |
+
$bridge = $this->initBridge();
|
2008 |
+
|
2009 |
+
//Create a new Bridge DTO Object
|
2010 |
+
$order_dto = $bridge->createOrder();
|
2011 |
+
|
2012 |
+
//Calculate date_placed
|
2013 |
+
$magento_order_created_at = $magento_order->getCreatedAt();
|
2014 |
+
$year = substr($magento_order_created_at, 0, 4);
|
2015 |
+
$month = substr($magento_order_created_at, 5, 2);
|
2016 |
+
$day = substr($magento_order_created_at, 8, 2);
|
2017 |
+
$hour = substr($magento_order_created_at, 11, 2);
|
2018 |
+
$minute = substr($magento_order_created_at, 14, 2);
|
2019 |
+
$second = substr($magento_order_created_at, 17, 2);
|
2020 |
+
|
2021 |
+
$date_placed = mktime($hour, $minute, $second, $month, $day, $year);
|
2022 |
+
|
2023 |
+
//Set main values
|
2024 |
+
$order_dto->setId($magento_order->getId())
|
2025 |
+
->setPublicId($magento_order->getIncrementId())
|
2026 |
+
->setChannelId($magento_order->getStoreId())
|
2027 |
+
->setOrderStatus($magento_order->getStatus())
|
2028 |
+
->setTotal((float)$magento_order->getGrandTotal())
|
2029 |
+
->setDatePlaced($date_placed);
|
2030 |
+
|
2031 |
+
//Add billing information
|
2032 |
+
$magento_billing_data = $magento_order->getBillingAddress()->getData(); //Array
|
2033 |
+
$billing_street = preg_split('/\n/', $magento_billing_data['street']);
|
2034 |
+
$order_dto->addBilling()
|
2035 |
+
->setFirstname(isset($magento_billing_data['firstname']{0}) ? $magento_billing_data['firstname'] : $magento_order->getCustomerFirstname())
|
2036 |
+
->setLastname(isset($magento_billing_data['lastname']{0}) ? $magento_billing_data['lastname'] : $magento_order->getCustomerLastname())
|
2037 |
+
->setCompany($magento_billing_data['company'])
|
2038 |
+
->setStreet($billing_street[0])
|
2039 |
+
->setSuburb(implode("\n", array_slice($billing_street, 1, count($billing_street))))
|
2040 |
+
->setCity($magento_billing_data['city'])
|
2041 |
+
->setCounty($magento_billing_data['region'])
|
2042 |
+
->setPostcode($magento_billing_data['postcode'])
|
2043 |
+
->setCountryIsoCode($magento_billing_data['country_id'])
|
2044 |
+
->setTelephone($magento_billing_data['telephone'])
|
2045 |
+
->setEmailAddress(isset($magento_billing_data['email']{0}) ? $magento_billing_data['email'] : $magento_order->getCustomerEmail());
|
2046 |
+
|
2047 |
+
//Add delivery information
|
2048 |
+
$magento_shipping_address = $magento_order->getShippingAddress();
|
2049 |
+
|
2050 |
+
//Check if shipping details exist, if not use billing data again
|
2051 |
+
$magento_shipping_data = $magento_shipping_address !== false ? $magento_shipping_address->getData() : $magento_billing_data;
|
2052 |
+
$shipping_street = preg_split('/\n/', $magento_shipping_data['street']);
|
2053 |
+
$order_dto->addDelivery()
|
2054 |
+
->setFirstname($magento_shipping_data['firstname'])
|
2055 |
+
->setLastname($magento_shipping_data['lastname'])
|
2056 |
+
->setCompany($magento_shipping_data['company'])
|
2057 |
+
->setStreet($shipping_street[0])
|
2058 |
+
->setSuburb(implode("\n", array_slice($shipping_street, 1, count($shipping_street))))
|
2059 |
+
->setCity($magento_shipping_data['city'])
|
2060 |
+
->setCounty($magento_shipping_data['region'])
|
2061 |
+
->setPostcode($magento_shipping_data['postcode'])
|
2062 |
+
->setCountryIsoCode($magento_shipping_data['country_id'])
|
2063 |
+
->setTelephone($magento_shipping_data['telephone'])
|
2064 |
+
->setEmailAddress(isset($magento_shipping_data['email']{0}) ? $magento_shipping_data['email'] : $magento_order->getCustomerEmail());
|
2065 |
+
|
2066 |
+
//Set payment details
|
2067 |
+
$magento_order_payment = $magento_order->getPayment();
|
2068 |
+
$magento_order_payment_data = $magento_order_payment->getData();
|
2069 |
+
$payment = $order_dto->addPayment();
|
2070 |
+
$payment->setMethod($magento_order_payment_data['method'])
|
2071 |
+
->setBaseCurrency($magento_order->getBaseCurrencyCode())
|
2072 |
+
->setCurrency($magento_order->getOrderCurrencyCode());
|
2073 |
+
|
2074 |
+
//Set payment details data
|
2075 |
+
switch ($magento_order_payment_data['method'])
|
2076 |
+
{
|
2077 |
+
case 'paypal_direct':
|
2078 |
+
case 'paypal_express':
|
2079 |
+
case 'paypal_standard':
|
2080 |
+
$payment->setMethod('paypal');
|
2081 |
+
|
2082 |
+
$paypal_details = $payment->addPaypalDetails();
|
2083 |
+
|
2084 |
+
$paypal_details->setPayerId($magento_order_payment_data['additional_information']['paypal_payer_id'])
|
2085 |
+
->setPayerEmailAddress($magento_order_payment_data['additional_information']['paypal_payer_email']);
|
2086 |
+
|
2087 |
+
if (Mage_Paypal_Model_Info::isPaymentSuccessful($magento_order_payment))
|
2088 |
+
{
|
2089 |
+
if ($payment_last_trans_id = $magento_order_payment->getLastTransId())
|
2090 |
+
{
|
2091 |
+
$paypal_details->setTxId($payment_last_trans_id);
|
2092 |
+
}
|
2093 |
+
|
2094 |
+
$paypal_details->setStatus('OK')->setStatusLabel('Payment Received');
|
2095 |
+
|
2096 |
+
$payment->setAmount((float)$magento_order_payment->getAmountPaid());
|
2097 |
+
$payment->setBaseAmount((float)$magento_order_payment->getBaseAmountPaid());
|
2098 |
+
$order_dto->setIsPaid(true);
|
2099 |
+
}
|
2100 |
+
|
2101 |
+
break;
|
2102 |
+
|
2103 |
+
//Barclays epdq
|
2104 |
+
case 'ops_cc':
|
2105 |
+
$payment->setMethod('epdq');
|
2106 |
+
|
2107 |
+
//Check for payment id
|
2108 |
+
if (isset($magento_order_payment_data['additional_information']['paymentId']))
|
2109 |
+
{
|
2110 |
+
$epdq_details = $payment->addEpdqDetails();
|
2111 |
+
|
2112 |
+
$epdq_details->setTxId($magento_order_payment_data['additional_information']['paymentId']);
|
2113 |
+
|
2114 |
+
if ($order_is_paid === true) //Success
|
2115 |
+
{
|
2116 |
+
$epdq_details->setStatus('OK')->setStatusLabel('Payment Received');
|
2117 |
+
$epdq_details->setCcBrand($magento_order_payment_data['additional_information']['CC_BRAND'])
|
2118 |
+
->setAavCheck($magento_order_payment_data['additional_information']['additionalScoringData']['AAVCHECK'])
|
2119 |
+
->setCvcCheck($magento_order_payment_data['additional_information']['additionalScoringData']['CVCCHECK']);
|
2120 |
+
|
2121 |
+
$payment->setAmount((float)$magento_order_payment->getAmountPaid());
|
2122 |
+
$payment->setBaseAmount((float)$magento_order_payment->getBaseAmountPaid());
|
2123 |
+
$order_dto->setIsPaid(true);
|
2124 |
+
}
|
2125 |
+
else
|
2126 |
+
{
|
2127 |
+
$epdq_details->setStatus('FAILED')->setStatusLabel('Payment failed. Status: ' . $magento_order_payment_data['additional_information']['status']);
|
2128 |
+
}
|
2129 |
+
}
|
2130 |
+
|
2131 |
+
break;
|
2132 |
+
|
2133 |
+
//Sagepay
|
2134 |
+
case 'sagepaydirectpro':
|
2135 |
+
case 'sagepayserver':
|
2136 |
+
case 'sagepayserver_moto':
|
2137 |
+
case 'sagepaypaypal':
|
2138 |
+
case 'sagepayform':
|
2139 |
+
|
2140 |
+
$payment->setMethod('sagepay');
|
2141 |
+
|
2142 |
+
$sagepay_data = Mage::getModel('sagepaysuite2/sagepaysuite_transaction')->getCollection()->addFieldToFilter('order_id', $magento_order->getId())->getFirstItem()->getData();
|
2143 |
+
|
2144 |
+
if (isset($sagepay_data['id']) && isset($sagepay_data['vps_tx_id']))
|
2145 |
+
{
|
2146 |
+
$sagepay_details = $payment->addSagepayDetails();
|
2147 |
+
$sagepay_details->setTxId($sagepay_data['vps_tx_id'])->setStatus('OK')->setStatusLabel('Payment success');
|
2148 |
+
$sagepay_details->setCv2Result($sagepay_data['cv2result'])
|
2149 |
+
->setAddressResult($sagepay_data['address_result'])
|
2150 |
+
->setPostcodeResult($sagepay_data['postcode_result'])
|
2151 |
+
->setAvsCv2Check($sagepay_data['avscv2'])
|
2152 |
+
->setAuthCode($sagepay_data['tx_auth_no'])
|
2153 |
+
->setThreeDSecureStatus($sagepay_data['threed_secure_status']);
|
2154 |
+
|
2155 |
+
$payment->setAmount((float)$magento_order_payment->getAmountPaid());
|
2156 |
+
$payment->setBaseAmount((float)$magento_order_payment->getBaseAmountPaid());
|
2157 |
+
$order_dto->setIsPaid(true);
|
2158 |
+
}
|
2159 |
+
else
|
2160 |
+
{
|
2161 |
+
//There is a weird issue with Sagepay plugin. Maybe it updates the status before it actually stores the data in the datbase
|
2162 |
+
//So an order is paid but we cant get sagepay details
|
2163 |
+
$payment->setAmount((float)$magento_order_payment->getAmountPaid());
|
2164 |
+
$payment->setBaseAmount((float)$magento_order_payment->getBaseAmountPaid());
|
2165 |
+
$order_is_paid = netmatter_float_equals($magento_order->getGrandTotal(), $magento_order_payment->getAmountPaid());
|
2166 |
+
$order_dto->setIsPaid($order_is_paid);
|
2167 |
+
}
|
2168 |
+
|
2169 |
+
break;
|
2170 |
+
|
2171 |
+
//Securetradingxpay
|
2172 |
+
case 'securetradingxpay':
|
2173 |
+
|
2174 |
+
$cc_approval = strlen($magento_order_payment_data['cc_approval']) >= 10 && substr($magento_order_payment_data['cc_approval'], 0, 10) === 'AUTH CODE:';
|
2175 |
+
|
2176 |
+
if (isset($magento_order_payment_data['cc_trans_id']{0}) && $cc_approval === true)
|
2177 |
+
{
|
2178 |
+
$default_details = $payment->addDefaultDetails();
|
2179 |
+
$default_details->setValue('card_type', $magento_order_payment_data['cc_type']);
|
2180 |
+
$default_details->setValue('cc_trans_id', $magento_order_payment_data['cc_trans_id']);
|
2181 |
+
$default_details->setStatus('OK')->setStatusLabel('Payment Received');
|
2182 |
+
|
2183 |
+
//All the amount is paid
|
2184 |
+
$payment->setAmount($magento_order_payment_data['amount_authorized']);
|
2185 |
+
$payment->setBaseAmount($magento_order_payment_data['base_amount_authorized']);
|
2186 |
+
|
2187 |
+
//Mark order as paid
|
2188 |
+
$order_dto->setIsPaid(true);
|
2189 |
+
}
|
2190 |
+
|
2191 |
+
break;
|
2192 |
+
|
2193 |
+
//Charity Clear
|
2194 |
+
case 'CharityClearHosted_standard':
|
2195 |
+
$payment->setMethod('charity_clear');
|
2196 |
+
|
2197 |
+
$charity_clear_data = Mage::getModel('CharityClearHosted/CharityClearHosted_Trans')->getCollection()->addFieldToFilter('orderid', $magento_order->getIncrementId())->getFirstItem()->getData();
|
2198 |
+
|
2199 |
+
$success = strlen($charity_clear_data['message']) >= 9 && substr($charity_clear_data['message'], 0, 9) === 'AUTHCODE:';
|
2200 |
+
|
2201 |
+
if (isset($magento_order_payment_data['last_trans_id']{0}) && $success === true && (string)$charity_clear_data['responsecode'] === '0')
|
2202 |
+
{
|
2203 |
+
$default_details = $payment->addDefaultDetails();
|
2204 |
+
$default_details->setStatus('OK')->setStatusLabel('Payment Received');
|
2205 |
+
|
2206 |
+
//All the amount is paid
|
2207 |
+
$payment->setAmount($magento_order_payment_data['amount_paid']);
|
2208 |
+
$payment->setBaseAmount($magento_order_payment_data['base_amount_paid']);
|
2209 |
+
|
2210 |
+
//Mark order as paid
|
2211 |
+
$order_dto->setIsPaid(true);
|
2212 |
+
}
|
2213 |
+
|
2214 |
+
break;
|
2215 |
+
|
2216 |
+
//Amazon payments
|
2217 |
+
case 'amazonpayments_advanced':
|
2218 |
+
$payment->setMethod('amazon_payments');
|
2219 |
+
if (isset($magento_order_payment_data['additional_information']['amazon_order_reference_id']{0}))
|
2220 |
+
{
|
2221 |
+
$default_details = $payment->addDefaultDetails();
|
2222 |
+
$default_details->setStatus('OK')->setStatusLabel('Payment Received');
|
2223 |
+
|
2224 |
+
//All the amount is paid
|
2225 |
+
$payment->setAmount($magento_order_payment_data['amount_paid']);
|
2226 |
+
$payment->setBaseAmount($magento_order_payment_data['base_amount_paid']);
|
2227 |
+
|
2228 |
+
//Mark order as paid
|
2229 |
+
$order_dto->setIsPaid(true);
|
2230 |
+
}
|
2231 |
+
|
2232 |
+
break;
|
2233 |
+
|
2234 |
+
//M2E Plugin
|
2235 |
+
case 'm2epropayment':
|
2236 |
+
|
2237 |
+
//Set default
|
2238 |
+
$payment->setMethod('m2epropayment');
|
2239 |
+
|
2240 |
+
if (isset($magento_order_payment_data['additional_data']))
|
2241 |
+
{
|
2242 |
+
$additional_data = unserialize($magento_order_payment_data['additional_data']);
|
2243 |
+
$sum = 0;
|
2244 |
+
|
2245 |
+
if (isset($additional_data['payment_method']))
|
2246 |
+
{
|
2247 |
+
if ($additional_data['payment_method'] === 'PayPal')
|
2248 |
+
{
|
2249 |
+
$payment->setMethod('paypal');
|
2250 |
+
}
|
2251 |
+
else
|
2252 |
+
{
|
2253 |
+
if (isset($additional_data['payment_method']{0}))
|
2254 |
+
{
|
2255 |
+
$payment->setMethod($additional_data['payment_method']);
|
2256 |
+
}
|
2257 |
+
else
|
2258 |
+
{
|
2259 |
+
$payment->setMethod($additional_data['component_mode']);
|
2260 |
+
}
|
2261 |
+
}
|
2262 |
+
}
|
2263 |
+
|
2264 |
+
if (count($additional_data['transactions']) > 0)
|
2265 |
+
{
|
2266 |
+
foreach ($additional_data['transactions'] AS $transaction)
|
2267 |
+
{
|
2268 |
+
$sum += $transaction['sum'];
|
2269 |
+
}
|
2270 |
+
}
|
2271 |
+
else
|
2272 |
+
{
|
2273 |
+
$sum = $magento_order_payment_data['base_amount_paid'];
|
2274 |
+
}
|
2275 |
+
|
2276 |
+
$order_is_paid = netmatter_float_equals($magento_order->getGrandTotal(), $sum);
|
2277 |
+
if ($order_is_paid)
|
2278 |
+
{
|
2279 |
+
$default_details = $payment->addDefaultDetails();
|
2280 |
+
if (count($additional_data['transactions']) > 0)
|
2281 |
+
{
|
2282 |
+
$default_details->setTxId($additional_data['transactions'][count($additional_data['transactions']) - 1]['transaction_id']);
|
2283 |
+
}
|
2284 |
+
else
|
2285 |
+
{
|
2286 |
+
$default_details->setTxId($additional_data['channel_order_id']);
|
2287 |
+
}
|
2288 |
+
$default_details->setStatus('OK')->setStatusLabel('Payment Received');
|
2289 |
+
|
2290 |
+
//All the amount is paid
|
2291 |
+
$payment->setAmount($magento_order_payment_data['amount_paid']);
|
2292 |
+
$payment->setBaseAmount($magento_order_payment_data['base_amount_paid']);
|
2293 |
+
|
2294 |
+
//Mark order as paid
|
2295 |
+
$order_dto->setIsPaid(true);
|
2296 |
+
}
|
2297 |
+
}
|
2298 |
+
|
2299 |
+
break;
|
2300 |
+
|
2301 |
+
case 'worldpay_cc':
|
2302 |
+
$order_is_paid = netmatter_float_equals($magento_order->getGrandTotal(), $magento_order_payment->getAmountPaid());
|
2303 |
+
$order_dto->setIsPaid($order_is_paid);
|
2304 |
+
|
2305 |
+
if ($order_is_paid)
|
2306 |
+
{
|
2307 |
+
$payment->setAmount((float)$magento_order_payment->getAmountPaid())
|
2308 |
+
->setBaseAmount((float)$magento_order_payment->getBaseAmountPaid());
|
2309 |
+
|
2310 |
+
$payment->addDefaultDetails()
|
2311 |
+
->setValue('card_type', $magento_order_payment_data['cc_type'])
|
2312 |
+
->setValue('cc_trans_id', $magento_order_payment_data['cc_trans_id'])
|
2313 |
+
->setStatus('OK')
|
2314 |
+
->setStatusLabel('Payment Received');
|
2315 |
+
}
|
2316 |
+
|
2317 |
+
break;
|
2318 |
+
|
2319 |
+
case 'realex':
|
2320 |
+
$payment->setAmount((float)$magento_order_payment->getAmountPaid())
|
2321 |
+
->setBaseAmount((float)$magento_order_payment->getBaseAmountPaid());
|
2322 |
+
|
2323 |
+
$order_dto->setIsPaid(true);
|
2324 |
+
|
2325 |
+
break;
|
2326 |
+
|
2327 |
+
case 'free':
|
2328 |
+
$payment->setAmount(0)
|
2329 |
+
->setBaseAmount(0);
|
2330 |
+
|
2331 |
+
$order_dto->setIsPaid(true);
|
2332 |
+
|
2333 |
+
break;
|
2334 |
+
|
2335 |
+
//Default
|
2336 |
+
default:
|
2337 |
+
$payment->setAmount((float)$magento_order_payment->getAmountPaid())
|
2338 |
+
->setBaseAmount((float)$magento_order_payment->getBaseAmountPaid());
|
2339 |
+
|
2340 |
+
$order_dto->setIsPaid($magento_order->getBaseTotalDue() == 0);
|
2341 |
+
}
|
2342 |
+
|
2343 |
+
//Set customer details
|
2344 |
+
$customer_firstname = $magento_order->getCustomerFirstname();
|
2345 |
+
$customer_lastname = $magento_order->getCustomerLastname();
|
2346 |
+
$customer_street = preg_split('/\n/', $magento_billing_data['street']);
|
2347 |
+
|
2348 |
+
$order_dto->addCustomer()
|
2349 |
+
->setId((int)$magento_order->getCustomerId())
|
2350 |
+
->setFirstname(isset($customer_firstname{0}) ? $customer_firstname : $magento_billing_data['firstname'])
|
2351 |
+
->setLastname(isset($customer_lastname{0}) ? $customer_lastname : $magento_billing_data['lastname'])
|
2352 |
+
->setEmailAddress($magento_order->getCustomerEmail())
|
2353 |
+
->setTelephone($magento_billing_data['telephone'])
|
2354 |
+
->setCompany($magento_billing_data['company'])
|
2355 |
+
->setStreet($customer_street[0])
|
2356 |
+
->setSuburb(implode("\n", array_slice($customer_street, 1, count($customer_street))))
|
2357 |
+
->setCity($magento_billing_data['city'])
|
2358 |
+
->setCounty($magento_billing_data['region'])
|
2359 |
+
->setPostcode($magento_billing_data['postcode'])
|
2360 |
+
->setCountryIsoCode($magento_billing_data['country_id']);
|
2361 |
+
|
2362 |
+
//Retrieve magento order items
|
2363 |
+
$magento_order_items = $magento_order->getAllItems();
|
2364 |
+
|
2365 |
+
//Retrieve magento order tax rates, and tax codes for each item
|
2366 |
+
$magento_order_tax_rates = array();
|
2367 |
+
$magento_order_items_tax = array();
|
2368 |
+
$magento_order_tax_rates_lines = Mage::getModel('tax/sales_order_tax')->getCollection()->loadByOrder($magento_order)->toArray();
|
2369 |
+
foreach ($magento_order_tax_rates_lines['items'] as $magento_order_tax_rates_line)
|
2370 |
+
{
|
2371 |
+
$magento_order_tax_rates[$magento_order_tax_rates_line['tax_id']] = $magento_order_tax_rates_line;
|
2372 |
+
|
2373 |
+
//Get items tax info
|
2374 |
+
$magento_order_items_tax_lines = Mage::getModel('tax/sales_order_tax_item')->getCollection()->addFieldToFilter('tax_id', $magento_order_tax_rates_line['tax_id'])->toArray();
|
2375 |
+
|
2376 |
+
foreach ($magento_order_items_tax_lines['items'] as $magento_order_items_tax_line)
|
2377 |
+
{
|
2378 |
+
$magento_order_items_tax[$magento_order_items_tax_line['item_id']] = array(
|
2379 |
+
'code' => $magento_order_tax_rates_line['code'],
|
2380 |
+
'percent' => $magento_order_tax_rates_line['percent']
|
2381 |
+
);
|
2382 |
+
}
|
2383 |
+
}
|
2384 |
+
|
2385 |
+
//Try to get the correct tax rate for shipping method
|
2386 |
+
$shipping_amount = $magento_order->getShippingAmount();
|
2387 |
+
if ($shipping_amount > 0)
|
2388 |
+
{
|
2389 |
+
$shipping_tax_rate = (float)(($magento_order->getShippingInclTax() - $magento_order->getShippingAmount()) / $magento_order->getShippingAmount() * 100.0);
|
2390 |
+
}
|
2391 |
+
else
|
2392 |
+
{
|
2393 |
+
$shipping_tax_rate = 0.0;
|
2394 |
+
}
|
2395 |
+
|
2396 |
+
$shipping_tax_code = null;
|
2397 |
+
foreach ($magento_order_tax_rates as $magento_order_tax_rate)
|
2398 |
+
{
|
2399 |
+
if (netmatter_float_equals((float)$magento_order_tax_rate['percent'], (float)$shipping_tax_rate))
|
2400 |
+
{
|
2401 |
+
$shipping_tax_code = $magento_order_tax_rate['code'];
|
2402 |
+
break;
|
2403 |
+
}
|
2404 |
+
}
|
2405 |
+
|
2406 |
+
$magento_order_discounts = array();
|
2407 |
+
|
2408 |
+
//Add order lines
|
2409 |
+
foreach ($magento_order_items as $item)
|
2410 |
+
{
|
2411 |
+
//When a configurable product is bought, 2 item lines are added, so we only need the conf line and not the simple
|
2412 |
+
if ($item->getParentItemId() > 0)
|
2413 |
+
{
|
2414 |
+
continue;
|
2415 |
+
}
|
2416 |
+
|
2417 |
+
$item_dto = $order_dto->addLineItem();
|
2418 |
+
|
2419 |
+
//Find item's tax info
|
2420 |
+
if (isset($magento_order_items_tax[$item->getId()]))
|
2421 |
+
{
|
2422 |
+
$item_tax_code = $magento_order_items_tax[$item->getId()]['code'];
|
2423 |
+
}
|
2424 |
+
else
|
2425 |
+
{
|
2426 |
+
$item_tax_code = 'TAX_CODE_NOT_SET';
|
2427 |
+
|
2428 |
+
foreach ($magento_order_tax_rates_lines['items'] as $magento_order_tax_rates_line)
|
2429 |
+
{
|
2430 |
+
if (netmatter_float_equals($item['tax_percent'], $magento_order_tax_rates_line['percent']))
|
2431 |
+
{
|
2432 |
+
$item_tax_code = $magento_order_tax_rates_line['code'];
|
2433 |
+
break;
|
2434 |
+
}
|
2435 |
+
}
|
2436 |
+
}
|
2437 |
+
|
2438 |
+
$item_sku = '';
|
2439 |
+
$item_name = '';
|
2440 |
+
$item_options = array();
|
2441 |
+
$item_product_options = $item->getProductOptions();
|
2442 |
+
|
2443 |
+
//Based on type we have to extract name and sku
|
2444 |
+
switch ($item->getProductType())
|
2445 |
+
{
|
2446 |
+
//Item is configurable product
|
2447 |
+
case 'configurable':
|
2448 |
+
$item_sku = $item_product_options['simple_sku'];
|
2449 |
+
$item_name = $item_product_options['simple_name'];
|
2450 |
+
|
2451 |
+
break;
|
2452 |
+
|
2453 |
+
default:
|
2454 |
+
$item_sku = $item->getSku();
|
2455 |
+
$item_name = $item->getName();
|
2456 |
+
}
|
2457 |
+
|
2458 |
+
//Add product options if set
|
2459 |
+
if (isset($item_product_options['options']))
|
2460 |
+
{
|
2461 |
+
foreach ($item_product_options['options'] as $item_product_options_option)
|
2462 |
+
{
|
2463 |
+
$item_options[$item_product_options_option['label']] = $item_product_options_option['print_value'];
|
2464 |
+
}
|
2465 |
+
}
|
2466 |
+
|
2467 |
+
$item_dto->setProductId($item->getProductId())
|
2468 |
+
->setName($item_name)
|
2469 |
+
->setSku($item_sku)
|
2470 |
+
->setQuantity($item->getQtyOrdered())
|
2471 |
+
->setRowNet($item->getRowTotal())
|
2472 |
+
->setRowGross($item->getRowTotalInclTax())
|
2473 |
+
->setRowTax($item->getRowTotalInclTax() - $item->getRowTotal())
|
2474 |
+
->setTaxCode($item_tax_code);
|
2475 |
+
|
2476 |
+
foreach ($item_options AS $key => $value)
|
2477 |
+
{
|
2478 |
+
$item_dto->addOption($key, $value);
|
2479 |
+
}
|
2480 |
+
|
2481 |
+
//Check if item has a discount
|
2482 |
+
$item_discount_amount = $item->getDiscountAmount();
|
2483 |
+
|
2484 |
+
if ($item_discount_amount > 0)
|
2485 |
+
{
|
2486 |
+
//Based on discount method, magento calculates values different. We have to find if the discount amount contains tax or not, in order to calculate the correct net and tax discount amounts
|
2487 |
+
$weee_helper = Mage::helper('weee');
|
2488 |
+
|
2489 |
+
//Check if item discount contains vat or not
|
2490 |
+
if (method_exists($weee_helper, 'getRowWeeeAmountAfterDiscount'))
|
2491 |
+
{
|
2492 |
+
$item_total_calculated = $item->getRowTotal() + $item->getTaxAmount() + $item->getHiddenTaxAmount() + Mage::helper('weee')->getRowWeeeAmountAfterDiscount($item) - $item->getDiscountAmount();
|
2493 |
+
}
|
2494 |
+
else
|
2495 |
+
{
|
2496 |
+
$item_total_calculated = $item->getRowTotal() + $item->getTaxAmount() + $item->getHiddenTaxAmount() + $item->getWeeeTaxAppliedRowAmount() - $item->getDiscountAmount();
|
2497 |
+
}
|
2498 |
+
|
2499 |
+
$item_discount_contains_tax = round($item->getRowTotalInclTax() - $item_discount_amount, 2) === round($item_total_calculated, 2);
|
2500 |
+
|
2501 |
+
if ($item_discount_contains_tax === true) //Discount contains tax
|
2502 |
+
{
|
2503 |
+
$net_discount = $item_discount_amount / (1.0 + (float)$item->getTaxPercent() / 100.0);
|
2504 |
+
$tax_discount = $item_discount_amount - $net_discount;
|
2505 |
+
}
|
2506 |
+
else
|
2507 |
+
{
|
2508 |
+
$net_discount = $item_discount_amount;
|
2509 |
+
$tax_discount = $item_discount_amount * ((float)$item->getTaxPercent() / 100.0);
|
2510 |
+
}
|
2511 |
+
|
2512 |
+
$magento_order_discounts[$item_tax_code][$item->getSku()] = array(
|
2513 |
+
'net' => $net_discount,
|
2514 |
+
'tax' => $tax_discount
|
2515 |
+
);
|
2516 |
+
}
|
2517 |
+
|
2518 |
+
//Check if item is bundle
|
2519 |
+
if ($item->getProductType() === 'bundle')
|
2520 |
+
{
|
2521 |
+
$bundle_product = Mage::getModel('catalog/product')->load($item->getProductId());
|
2522 |
+
if ($bundle_product->getSkuType() === '0')
|
2523 |
+
{
|
2524 |
+
//Get bundle product's products
|
2525 |
+
$bundleSelectionsCollection = $bundle_product->getTypeInstance(true)->getSelectionsCollection(
|
2526 |
+
$bundle_product->getTypeInstance(true)->getOptionsIds($bundle_product), $bundle_product
|
2527 |
+
);
|
2528 |
+
|
2529 |
+
$bundled_items = array();
|
2530 |
+
foreach($bundleSelectionsCollection as $option)
|
2531 |
+
{
|
2532 |
+
$bundled_items[$option->option_id][$option->selection_id] = array(
|
2533 |
+
'product_id' => $option->product_id,
|
2534 |
+
'sku' => $option->sku,
|
2535 |
+
'name' => $option->name
|
2536 |
+
);
|
2537 |
+
}
|
2538 |
+
|
2539 |
+
$bundle_data = unserialize($item->getData('product_options'));
|
2540 |
+
|
2541 |
+
//Add bundle options as separate order lines
|
2542 |
+
foreach ($bundle_data['bundle_options'] AS $bundle_option)
|
2543 |
+
{
|
2544 |
+
foreach ($bundle_option['value'] AS $bundle_option_value_key => $bundle_option_value)
|
2545 |
+
{
|
2546 |
+
$option_value_id = $bundle_data['info_buyRequest']['bundle_option'][$bundle_option['option_id']];
|
2547 |
+
if (is_array($option_value_id))
|
2548 |
+
{
|
2549 |
+
$option_value_id = $option_value_id[$bundle_option_value_key];
|
2550 |
+
}
|
2551 |
+
|
2552 |
+
$bundled_item = $bundled_items[$bundle_option['option_id']][$option_value_id];
|
2553 |
+
|
2554 |
+
$bundle_item_dto = $order_dto->addLineItem();
|
2555 |
+
$bundle_item_dto->setProductId($bundled_item['product_id'])
|
2556 |
+
->setName($bundled_item['name'])
|
2557 |
+
->setSku($bundled_item['sku'])
|
2558 |
+
->setQuantity($bundle_option_value['qty'] * $item->getQtyOrdered())
|
2559 |
+
->setRowNet(0.00)
|
2560 |
+
->setRowGross(0.00)
|
2561 |
+
->setRowTax(0.00)
|
2562 |
+
->setTaxCode($item_tax_code);
|
2563 |
+
}
|
2564 |
+
}
|
2565 |
+
}
|
2566 |
+
}
|
2567 |
+
}
|
2568 |
+
|
2569 |
+
//Check for shipping discount
|
2570 |
+
$shipping_discount_amount = $magento_order->getShippingDiscountAmount();
|
2571 |
+
if ($shipping_discount_amount > 0)
|
2572 |
+
{
|
2573 |
+
$shipping_discount_tax_code = isset($shipping_tax_code) ? $shipping_tax_code : 'order-discount';
|
2574 |
+
|
2575 |
+
$magento_order_discounts[$shipping_discount_tax_code]['shipping_cost'] = array(
|
2576 |
+
'net' => $magento_order->getShippingDiscountAmount(),
|
2577 |
+
'tax' => ($magento_order->getShippingInclTax() - $magento_order->getShippingAmount()) - $magento_order->getShippingTaxAmount()
|
2578 |
+
);
|
2579 |
+
}
|
2580 |
+
|
2581 |
+
//Combine discounts with same tax codes
|
2582 |
+
$order_discounts = array();
|
2583 |
+
foreach($magento_order_discounts AS $tax_code => $magento_order_discount)
|
2584 |
+
{
|
2585 |
+
$discount_label = trim($magento_order->getDiscountDescription());
|
2586 |
+
if ($discount_label === '')
|
2587 |
+
{
|
2588 |
+
$discount_label = 'Order discount';
|
2589 |
+
}
|
2590 |
+
|
2591 |
+
//Calculate net, tax and gross
|
2592 |
+
$net = 0.0;
|
2593 |
+
$tax = 0.0;
|
2594 |
+
foreach ($magento_order_discount as $d)
|
2595 |
+
{
|
2596 |
+
$net += $d['net'];
|
2597 |
+
$tax += $d['tax'];
|
2598 |
+
}
|
2599 |
+
|
2600 |
+
if (!isset($order_discounts[$tax_code]))
|
2601 |
+
{
|
2602 |
+
$order_discounts[$tax_code] = array(
|
2603 |
+
'label' => $discount_label,
|
2604 |
+
'net' => 0.00,
|
2605 |
+
'tax' => 0.00,
|
2606 |
+
'gross' => 0.00
|
2607 |
+
);
|
2608 |
+
}
|
2609 |
+
|
2610 |
+
$order_discounts[$tax_code]['net'] += $net;
|
2611 |
+
$order_discounts[$tax_code]['tax'] += $tax;
|
2612 |
+
$order_discounts[$tax_code]['gross'] += $net + $tax;
|
2613 |
+
if ($order_discounts[$tax_code]['label'] !== $discount_label)
|
2614 |
+
{
|
2615 |
+
$order_discounts[$tax_code]['label'] .= ', ' . $discount_label;
|
2616 |
+
}
|
2617 |
+
}
|
2618 |
+
|
2619 |
+
//Append discounts to DTO
|
2620 |
+
foreach ($order_discounts as $order_discount_tax_code => $order_discount)
|
2621 |
+
{
|
2622 |
+
$discount = $order_dto->addDiscount();
|
2623 |
+
$discount->setTaxCode($order_discount_tax_code);
|
2624 |
+
$discount->setLabel($order_discount['label']);
|
2625 |
+
$discount->setNet($order_discount['net'])->setTax($order_discount['tax'])->setGross($order_discount['gross']);
|
2626 |
+
}
|
2627 |
+
|
2628 |
+
//Add shipping
|
2629 |
+
$order_shipping_dto = $order_dto->addShipping();
|
2630 |
+
$order_shipping_dto->setMethod($magento_order->getShippingMethod())
|
2631 |
+
->setMethodLabel($magento_order->getShippingDescription())
|
2632 |
+
->setNet($magento_order->getShippingAmount())
|
2633 |
+
->setTax($magento_order->getShippingInclTax() - $magento_order->getShippingAmount())
|
2634 |
+
->setGross($magento_order->getShippingInclTax())
|
2635 |
+
->setTaxCode($shipping_tax_code);
|
2636 |
+
|
2637 |
+
//Check for gift message
|
2638 |
+
$gift_message_id = $magento_order->getGiftMessageId();
|
2639 |
+
if ($gift_message_id > 0)
|
2640 |
+
{
|
2641 |
+
$gift_message = Mage::getModel('giftmessage/message')->load($gift_message_id);
|
2642 |
+
$order_shipping_dto->setGiftMessage($gift_message->getMessage(), $gift_message->getRecipient(), $gift_message->getSender());
|
2643 |
+
}
|
2644 |
+
|
2645 |
+
//Send the order to the Bridge
|
2646 |
+
return $bridge->sendOrder($order_dto) === true;
|
2647 |
+
}
|
2648 |
+
|
2649 |
+
/**
|
2650 |
+
* Checks if the given product id exists for the given website id
|
2651 |
+
*
|
2652 |
+
* @param $product_id the id of the product
|
2653 |
+
* @param $website_id the id of the store
|
2654 |
+
* @return boolean
|
2655 |
+
*/
|
2656 |
+
public function productWebsiteExists($product_id, $website)
|
2657 |
+
{
|
2658 |
+
$result = $this->db()->query("SELECT 1 FROM " . $this->getTablePrefix() . "catalog_product_website WHERE product_id = :product_id AND website_id = :website_id;", array('product_id' => $product_id, 'website_id' => $website_id));
|
2659 |
+
$row = $result->fetch(PDO::FETCH_ASSOC);
|
2660 |
+
return $row !== false;
|
2661 |
+
}
|
2662 |
+
}
|
app/code/community/Netmatter/Bridge/Model/Order/Observer.php
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @class Netmatter_Bridge_Model_Order_Observer
|
4 |
+
*
|
5 |
+
* Contains observers that are executed to send the order to bridge
|
6 |
+
*/
|
7 |
+
class Netmatter_Bridge_Model_Order_Observer
|
8 |
+
{
|
9 |
+
/**
|
10 |
+
* Sends the order to the bridge
|
11 |
+
*
|
12 |
+
* @param $magento_order the order to send
|
13 |
+
* @return self
|
14 |
+
*/
|
15 |
+
private function sendOrder($magento_order)
|
16 |
+
{
|
17 |
+
//Check flag
|
18 |
+
if (Mage::registry('netmatter_bridge_disable_order_observer') === 1)
|
19 |
+
{
|
20 |
+
return $this;
|
21 |
+
}
|
22 |
+
|
23 |
+
Mage::helper('bridge/data')->sendOrder($magento_order);
|
24 |
+
|
25 |
+
return $this;
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Handles new order placement event.
|
30 |
+
*
|
31 |
+
* Passes order information and customer information to the Bridge.
|
32 |
+
*
|
33 |
+
* This method is called internally when the Magento event, 'sales_order_place_after' is dispatched.
|
34 |
+
*
|
35 |
+
* @param Varien_Event_Observer A magento event object
|
36 |
+
* @return self
|
37 |
+
*/
|
38 |
+
public function sales_order_place_after(Varien_Event_Observer $observer)
|
39 |
+
{
|
40 |
+
return $this->sendOrder($observer->getEvent()->getOrder());
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Handles order save after event
|
45 |
+
*
|
46 |
+
* @param Varien_Event_Observer A magento event object
|
47 |
+
* @return self
|
48 |
+
*/
|
49 |
+
public function sales_order_save_after(Varien_Event_Observer $observer)
|
50 |
+
{
|
51 |
+
$order = $observer->getEvent()->getOrder();
|
52 |
+
|
53 |
+
if ($order->dataHasChangedFor('status') !== true)
|
54 |
+
{
|
55 |
+
return $this;
|
56 |
+
}
|
57 |
+
|
58 |
+
return $this->sendOrder($observer->getEvent()->getOrder());
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Handles order payment event
|
63 |
+
*
|
64 |
+
* Passes order information and customer information to the Bridge.
|
65 |
+
*
|
66 |
+
* This method is called internally when the Magento event, 'sales_order_payment_pay' is dispatched.
|
67 |
+
*
|
68 |
+
* @param Varien_Event_Observer A magento event object
|
69 |
+
* @return self
|
70 |
+
*/
|
71 |
+
public function sales_order_payment_pay(Varien_Event_Observer $observer)
|
72 |
+
{
|
73 |
+
return $this->sendOrder($observer->getEvent()->getPayment()->getOrder());
|
74 |
+
}
|
75 |
+
}
|
app/code/community/Netmatter/Bridge/controllers/CallbackController.php
ADDED
@@ -0,0 +1,649 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file contains an callback controler that listens for
|
4 |
+
* requests from the Bridge and dispatches them to
|
5 |
+
*
|
6 |
+
* @category Netmatter
|
7 |
+
* @package Netmatter_Bridge
|
8 |
+
* @author Netmatter Team
|
9 |
+
* @copyright Copyright (c) 2008-2014 Netmatter Ltd. (http://www.netmatter.co.uk)
|
10 |
+
* @see Netmatter/etc/config.xml For events that are captured
|
11 |
+
*/
|
12 |
+
class Netmatter_Bridge_CallbackController extends Mage_Core_Controller_Front_Action
|
13 |
+
{
|
14 |
+
/** @var Netmatter_Bridge_Bridge */
|
15 |
+
private $bridge;
|
16 |
+
|
17 |
+
/** @var Netmatter_Bridge_Helper_Data */
|
18 |
+
private $functions;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* This method can be accessed by hitting
|
22 |
+
*
|
23 |
+
* Url: /netmatter/callback
|
24 |
+
*
|
25 |
+
* It expects POST data from the bridge, and can internally redirect
|
26 |
+
* to the correct action.
|
27 |
+
*
|
28 |
+
* @return void
|
29 |
+
*/
|
30 |
+
public function indexAction()
|
31 |
+
{
|
32 |
+
//Include helper functions
|
33 |
+
$this->functions = Mage::helper('bridge/data');
|
34 |
+
|
35 |
+
//Check if integration is enabled
|
36 |
+
if (!$this->functions->isEnabled())
|
37 |
+
{
|
38 |
+
echo '{"errors": "Plugin not enabled"}';
|
39 |
+
return;
|
40 |
+
}
|
41 |
+
|
42 |
+
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
|
43 |
+
|
44 |
+
//Include standard plugin
|
45 |
+
$bridge = $this->functions->initBridge();
|
46 |
+
|
47 |
+
//Define callback functions
|
48 |
+
$bridge->registerCallback('product_stock_modified', array($this, 'callbackBridgeProductStockModified'));
|
49 |
+
$bridge->registerCallback('order_status_modified', array($this, 'callbackBridgeOrderStatusModified'));
|
50 |
+
$bridge->registerCallback('product_modified', array($this, 'callbackBridgeProductModified'));
|
51 |
+
$bridge->registerCallback('configuration_get', array($this, 'callbackBridgeConfigurationGet'));
|
52 |
+
|
53 |
+
//Listen for requests
|
54 |
+
$bridge->listen();
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Callback method that handles the Product Stock Modified event
|
59 |
+
*
|
60 |
+
* It updates the quantities of the given product
|
61 |
+
*
|
62 |
+
* @param array $product an array containing id and stock
|
63 |
+
* @return empty array on success, an array containing the errors on failure
|
64 |
+
*/
|
65 |
+
public function callbackBridgeProductStockModified(array $product)
|
66 |
+
{
|
67 |
+
//Retrieve product ids based on given sku
|
68 |
+
$ids = $this->functions->getProductIdsBySku($product['sku']);
|
69 |
+
|
70 |
+
//Make checks
|
71 |
+
$count_ids = count($ids);
|
72 |
+
if ($count_ids > 1)
|
73 |
+
{
|
74 |
+
return array('numfound' => count($ids));
|
75 |
+
}
|
76 |
+
|
77 |
+
//Update product stock
|
78 |
+
$this->functions->setProductStockById($ids[0], $product['stock']);
|
79 |
+
|
80 |
+
//Success
|
81 |
+
return array();
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Callback method that handles the Order status modified event
|
86 |
+
*
|
87 |
+
* It updates the status of the given order
|
88 |
+
*
|
89 |
+
* @param array $order an array containing id and statusId
|
90 |
+
* @return empty array on success, an array containing the errors on failure
|
91 |
+
*/
|
92 |
+
public function callbackBridgeOrderStatusModified(array $order)
|
93 |
+
{
|
94 |
+
$mg_order = Mage::getModel("sales/order")->load($order['id']);
|
95 |
+
|
96 |
+
//Check if order exists
|
97 |
+
if ($mg_order->getId() === null)
|
98 |
+
{
|
99 |
+
return array('numfound' => 0);
|
100 |
+
}
|
101 |
+
|
102 |
+
$tracking_codes = array();
|
103 |
+
|
104 |
+
if (isset($order['shipments']))
|
105 |
+
{
|
106 |
+
foreach ($order['shipments'] AS $key => $shipment)
|
107 |
+
{
|
108 |
+
if (!isset($shipment['shippedOn'])) //Check for the shippedOn flag
|
109 |
+
{
|
110 |
+
unset($order['shipments'][$key]);
|
111 |
+
continue;
|
112 |
+
}
|
113 |
+
|
114 |
+
$tracking_codes[$shipment['shippingMethod']] = $shipment['reference'];
|
115 |
+
}
|
116 |
+
|
117 |
+
if (count($order['shipments']) > 0)
|
118 |
+
{
|
119 |
+
$this->functions->syncShipments($mg_order, $order['shipments']);
|
120 |
+
}
|
121 |
+
}
|
122 |
+
|
123 |
+
//Update order's status
|
124 |
+
$this->functions->setOrderStatus($mg_order, $order['statusId']);
|
125 |
+
|
126 |
+
//Set order's tracking codes
|
127 |
+
if (count($tracking_codes) > 0)
|
128 |
+
{
|
129 |
+
$shipment_hashes = array();
|
130 |
+
foreach ($order['shipments'] AS $shipment)
|
131 |
+
{
|
132 |
+
if (!isset($shipment['reference']))
|
133 |
+
{
|
134 |
+
continue;
|
135 |
+
}
|
136 |
+
|
137 |
+
$shipment_hashes[$shipment['reference']] = $this->functions->calculateShipmentHash($shipment);
|
138 |
+
}
|
139 |
+
|
140 |
+
$this->functions->setOrderTrackingCodes($mg_order, $tracking_codes, $shipment_hashes);
|
141 |
+
}
|
142 |
+
|
143 |
+
//Success
|
144 |
+
return array();
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Callback method that handles the Product Modified event
|
149 |
+
*
|
150 |
+
* It updates the given product
|
151 |
+
*
|
152 |
+
* @param array $product an array containing product details
|
153 |
+
* @return empty array on success, an array containing the errors on failure
|
154 |
+
*/
|
155 |
+
public function callbackBridgeProductModified(array $product)
|
156 |
+
{
|
157 |
+
//Retrieve product ids based on given sku
|
158 |
+
$ids = $this->functions->getProductIdsBySku($product['sku']);
|
159 |
+
|
160 |
+
//Make checks
|
161 |
+
$count_ids = count($ids);
|
162 |
+
if ($count_ids > 1)
|
163 |
+
{
|
164 |
+
return array('numfound' => count($ids));
|
165 |
+
}
|
166 |
+
|
167 |
+
//Retrieve available websites
|
168 |
+
$websites = $this->functions->getWebsites();
|
169 |
+
$website_ids = array_keys($websites);
|
170 |
+
|
171 |
+
//Linked key is present, so linked is true
|
172 |
+
if (!isset($product['linked']))
|
173 |
+
{
|
174 |
+
foreach ($websites as $website_id => $store_id)
|
175 |
+
{
|
176 |
+
$product['linked'][$website_id] = 'Yes';
|
177 |
+
}
|
178 |
+
}
|
179 |
+
|
180 |
+
$linked_websites = array();
|
181 |
+
foreach ($product['linked'] as $website_id => $value)
|
182 |
+
{
|
183 |
+
if (!in_array($website_id, $website_ids)) //Website not exists
|
184 |
+
{
|
185 |
+
continue;
|
186 |
+
}
|
187 |
+
|
188 |
+
if ($product['linked'][$website_id] === 'Yes')
|
189 |
+
{
|
190 |
+
$linked_websites[$websites[$website_id]] = $website_id;
|
191 |
+
}
|
192 |
+
}
|
193 |
+
|
194 |
+
//Check if linked is active for at least one website. If not we should not continue
|
195 |
+
if (count($linked_websites) === 0)
|
196 |
+
{
|
197 |
+
return null;
|
198 |
+
}
|
199 |
+
|
200 |
+
//Check if product exists
|
201 |
+
if ($count_ids === 1)
|
202 |
+
{
|
203 |
+
$product['id'] = $ids[0];
|
204 |
+
}
|
205 |
+
else //Product not exists
|
206 |
+
{
|
207 |
+
//Check the product
|
208 |
+
$product['id'] = $this->createProduct($product);
|
209 |
+
}
|
210 |
+
|
211 |
+
$mg_product = $this->functions->getProduct($product['id']);
|
212 |
+
|
213 |
+
$sync_product_categories = isset($product['categories']) && is_array($product['categories']);
|
214 |
+
$sync_product_images = isset($product['images']) && is_array($product['images']);
|
215 |
+
|
216 |
+
//Make sure that product is linked on all given websites
|
217 |
+
$product_website_ids = $mg_product->getWebsiteIds();
|
218 |
+
if ($sync_product_categories)
|
219 |
+
{
|
220 |
+
$mg_product->getCategoryIds(); //Load categories to product
|
221 |
+
}
|
222 |
+
if ($sync_product_images)
|
223 |
+
{
|
224 |
+
$mg_product->getResource()->getAttribute('media_gallery')->getBackend()->afterLoad($mg_product);
|
225 |
+
}
|
226 |
+
$mg_product_orig_data = $mg_product->getData();
|
227 |
+
foreach ($linked_websites AS $website_id)
|
228 |
+
{
|
229 |
+
if (!in_array($website_id, $product_website_ids))
|
230 |
+
{
|
231 |
+
$new_website_ids = array_values(array_unique(array_merge($product_website_ids, array_values($linked_websites))));
|
232 |
+
$mg_product->setWebsiteIds($new_website_ids);
|
233 |
+
break;
|
234 |
+
}
|
235 |
+
}
|
236 |
+
|
237 |
+
//Sync product attribute set
|
238 |
+
if (isset($product['productGroup']{0}))
|
239 |
+
{
|
240 |
+
$this->functions->syncProductAttributeSet($mg_product, $product['productGroup']);
|
241 |
+
}
|
242 |
+
|
243 |
+
//Sync product attributes
|
244 |
+
$attributes_type = array();
|
245 |
+
|
246 |
+
$product['attributes']['name'] = $product['name'];
|
247 |
+
$product['attributes']['description'] = $product['description']['text'];
|
248 |
+
$product['attributes']['short_description'] = $product['shortDescription']['text'];
|
249 |
+
$product['attributes']['weight'] = isset($product['weight']) ? $product['weight'] : 0.000;
|
250 |
+
|
251 |
+
if (isset($product['brandId']))
|
252 |
+
{
|
253 |
+
$product['attributes']['manufacturer'] = $product['brandId'];
|
254 |
+
}
|
255 |
+
if (isset($product['condition']))
|
256 |
+
{
|
257 |
+
$product['attributes']['condition'] = $product['condition'];
|
258 |
+
}
|
259 |
+
|
260 |
+
//Add identity
|
261 |
+
if (isset($product['identity']))
|
262 |
+
{
|
263 |
+
foreach ((array)$product['identity'] AS $product_identity_key => $product_identity_value)
|
264 |
+
{
|
265 |
+
if ($product_identity_key === 'sku')
|
266 |
+
{
|
267 |
+
continue;
|
268 |
+
}
|
269 |
+
$product['attributes'][$product_identity_key] = $product_identity_value;
|
270 |
+
$attributes_type[$product_identity_key] = 'identity';
|
271 |
+
}
|
272 |
+
}
|
273 |
+
|
274 |
+
//Add physical
|
275 |
+
if (isset($product['physical']))
|
276 |
+
{
|
277 |
+
foreach ((array)$product['physical'] AS $product_physical_key => $product_physical_value)
|
278 |
+
{
|
279 |
+
$product['attributes'][$product_physical_key] = $product_physical_value;
|
280 |
+
$attributes_type[$product_physical_key] = 'physical';
|
281 |
+
}
|
282 |
+
}
|
283 |
+
|
284 |
+
//Add all options as attributes dropdown
|
285 |
+
if (isset($product['options']))
|
286 |
+
{
|
287 |
+
foreach ($product['options'] AS $product_option_key => $product_option_value)
|
288 |
+
{
|
289 |
+
$attributes_type[$product_option_key] = 'option';
|
290 |
+
$product['attributes'][$product_option_key] = $product_option_value;
|
291 |
+
}
|
292 |
+
}
|
293 |
+
|
294 |
+
$this->functions->syncProductAttributes($mg_product, $product['attributes'], $attributes_type);
|
295 |
+
|
296 |
+
//Sync product tax code
|
297 |
+
if (isset($product['taxCode']))
|
298 |
+
{
|
299 |
+
if ($product['taxCode'] === 'None') //Magento special tax name
|
300 |
+
{
|
301 |
+
$mg_product->setTaxClassId(0);
|
302 |
+
}
|
303 |
+
else
|
304 |
+
{
|
305 |
+
//Only set if tax code exists
|
306 |
+
$product_tax_class = $this->functions->getProductTaxClassByName($product['taxCode']);
|
307 |
+
if ($product_tax_class !== null && count($product_tax_class) > 0)
|
308 |
+
{
|
309 |
+
$mg_product->setTaxClassId($product_tax_class['class_id']);
|
310 |
+
}
|
311 |
+
}
|
312 |
+
}
|
313 |
+
|
314 |
+
//Sync product categories
|
315 |
+
if ($sync_product_categories)
|
316 |
+
{
|
317 |
+
$this->functions->syncProductCategories($mg_product, $product['categories']);
|
318 |
+
}
|
319 |
+
|
320 |
+
//Sync product images
|
321 |
+
if ($sync_product_images)
|
322 |
+
{
|
323 |
+
$this->functions->syncProductImages($mg_product, $product['images']);
|
324 |
+
}
|
325 |
+
|
326 |
+
//Check if should trigger save on the product
|
327 |
+
if ($mg_product->hasDataChanges())
|
328 |
+
{
|
329 |
+
$product_updates = array_diff_assoc($mg_product->getData(), $mg_product_orig_data);
|
330 |
+
$product_updates = $this->array_recursive_diff($mg_product->getData(), $mg_product_orig_data);
|
331 |
+
|
332 |
+
if (count($product_updates) > 0) //We should trigger product save
|
333 |
+
{
|
334 |
+
$mg_product->save();
|
335 |
+
}
|
336 |
+
}
|
337 |
+
|
338 |
+
//Set product's stock
|
339 |
+
if (isset($product['stock']))
|
340 |
+
{
|
341 |
+
$this->functions->setProductStockById($product['id'], $product['stock']);
|
342 |
+
}
|
343 |
+
|
344 |
+
//Update product prices
|
345 |
+
if (isset($product['prices']))
|
346 |
+
{
|
347 |
+
$pricing_websites = $this->functions->getPricingWebsites($linked_websites, $websites);
|
348 |
+
//Format product prices
|
349 |
+
$product['prices'] = $this->formatProductPrices($product['prices'], $pricing_websites);
|
350 |
+
|
351 |
+
$this->functions->setSimpleProductPrices($product['id'], $product['prices'], $pricing_websites);
|
352 |
+
}
|
353 |
+
|
354 |
+
//Success
|
355 |
+
return $product['id'];
|
356 |
+
}
|
357 |
+
|
358 |
+
/**
|
359 |
+
* Creates a product
|
360 |
+
*
|
361 |
+
* @param array $product an array containing the new product's details
|
362 |
+
* @return the id of the product's cart id
|
363 |
+
*/
|
364 |
+
public function createProduct(array $product)
|
365 |
+
{
|
366 |
+
//Retrieve available product attributes
|
367 |
+
$attributes = $this->functions->getProductAttributes();
|
368 |
+
|
369 |
+
$cart_product_id = null;
|
370 |
+
$mg_product = Mage::getModel('catalog/product');
|
371 |
+
|
372 |
+
if (isset($product['productGroup']{0}))
|
373 |
+
{
|
374 |
+
$attribute_set_id = $this->functions->getAttributeSetIdByName($product['productGroup'], true); //Create if id doesnt exist
|
375 |
+
}
|
376 |
+
else
|
377 |
+
{
|
378 |
+
$attribute_set_id = $this->functions->getProductDefaultAttributeSetId();
|
379 |
+
}
|
380 |
+
|
381 |
+
$product_default_tax_class = $this->functions->getProductDefaultTaxClass();
|
382 |
+
|
383 |
+
//Set product main details
|
384 |
+
$mg_product->setTypeId('simple')
|
385 |
+
->setCreatedAt(strtotime('now'))
|
386 |
+
->setSku($product['sku'])
|
387 |
+
->setName($product['name'])
|
388 |
+
->setPrice(0.00)
|
389 |
+
->setStatus(Mage_Catalog_Model_Product_Status::STATUS_DISABLED)
|
390 |
+
->setVisibility(Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH) //Catalog and search visibility
|
391 |
+
->setWebsiteIds($product['website_ids'])
|
392 |
+
->setTaxClassId($product_default_tax_class['class_id'])
|
393 |
+
->setAttributeSetId($attribute_set_id);
|
394 |
+
|
395 |
+
//Save product
|
396 |
+
$mg_product->save();
|
397 |
+
|
398 |
+
//Retrieve new product's id
|
399 |
+
$cart_product_id = $mg_product->getId();
|
400 |
+
|
401 |
+
if ($cart_product_id === null) //Check if product is created or not
|
402 |
+
throw new Exception('Product could not be created');
|
403 |
+
|
404 |
+
return $cart_product_id;
|
405 |
+
}
|
406 |
+
|
407 |
+
/**
|
408 |
+
* Formats the given array of prices so it converts retail to retail-1, retail-2 etc
|
409 |
+
*
|
410 |
+
* It does not override shop specific prices when are present
|
411 |
+
*
|
412 |
+
* @param array $prices an array containing the prices
|
413 |
+
* @param array $shops an array containing the shops
|
414 |
+
* @return the formatted array
|
415 |
+
*/
|
416 |
+
public function formatProductPrices(array $prices, array $shops)
|
417 |
+
{
|
418 |
+
foreach (array('cost', 'rrp', 'sale', 'retail') AS $pricekey)
|
419 |
+
{
|
420 |
+
if (isset($prices[$pricekey]))
|
421 |
+
{
|
422 |
+
foreach ($shops AS $shop => $set)
|
423 |
+
{
|
424 |
+
if (!isset($prices[$pricekey . '-' . $set]))
|
425 |
+
{
|
426 |
+
$prices[$pricekey . '-' . $set] = $prices[$pricekey];
|
427 |
+
}
|
428 |
+
}
|
429 |
+
|
430 |
+
unset($prices[$pricekey]);
|
431 |
+
}
|
432 |
+
|
433 |
+
foreach ($shops AS $shop => $set)
|
434 |
+
{
|
435 |
+
if ($shop === $set)
|
436 |
+
{
|
437 |
+
continue;
|
438 |
+
}
|
439 |
+
|
440 |
+
if (isset($prices[$pricekey . '-' . $shop]))
|
441 |
+
{
|
442 |
+
$prices[$pricekey . '-' . $set] = $prices[$pricekey . '-' . $shop];
|
443 |
+
unset($prices[$pricekey . '-' . $shop]);
|
444 |
+
}
|
445 |
+
}
|
446 |
+
}
|
447 |
+
|
448 |
+
if (isset($prices[$pricekey . '-0']))
|
449 |
+
{
|
450 |
+
foreach ($prices[$pricekey . '-0'] AS $qty => $value)
|
451 |
+
{
|
452 |
+
if ($qty > 1)
|
453 |
+
{
|
454 |
+
unset($prices[$pricekey . '-0'][$qty]);
|
455 |
+
}
|
456 |
+
}
|
457 |
+
}
|
458 |
+
|
459 |
+
//Check for groups prices
|
460 |
+
$groups = $this->functions->getGroups();
|
461 |
+
foreach ($groups AS $group_id)
|
462 |
+
{
|
463 |
+
if (isset($prices['group_' . $group_id]))
|
464 |
+
{
|
465 |
+
foreach ($shops AS $shop => $set)
|
466 |
+
{
|
467 |
+
if ($shop === 0) //Remove values from groups from zero 0 because magento bugs. If a default price is used, we copy the prices to all the enabled websites
|
468 |
+
{
|
469 |
+
continue;
|
470 |
+
}
|
471 |
+
|
472 |
+
if (!isset($prices['group_' . $group_id . '-' . $set]))
|
473 |
+
{
|
474 |
+
$prices['group_' . $group_id . '-' . $set] = $prices['group_' . $group_id];
|
475 |
+
}
|
476 |
+
}
|
477 |
+
|
478 |
+
unset($prices['group_' . $group_id]);
|
479 |
+
}
|
480 |
+
|
481 |
+
foreach ($shops AS $shop => $set)
|
482 |
+
{
|
483 |
+
if ($shop === $set)
|
484 |
+
{
|
485 |
+
continue;
|
486 |
+
}
|
487 |
+
|
488 |
+
if (isset($prices['group_' . $group_id . '-' . $shop]))
|
489 |
+
{
|
490 |
+
$prices['group_' . $group_id . '-' . $set] = $prices['group_' . $group_id . '-' . $shop];
|
491 |
+
unset($prices['group_' . $group_id . '-' . $shop]);
|
492 |
+
}
|
493 |
+
}
|
494 |
+
}
|
495 |
+
|
496 |
+
return $prices;
|
497 |
+
}
|
498 |
+
|
499 |
+
function array_recursive_diff($array1, $array2)
|
500 |
+
{
|
501 |
+
$ret = array();
|
502 |
+
|
503 |
+
foreach ($array1 as $key => $value)
|
504 |
+
{
|
505 |
+
if (array_key_exists($key, $array2))
|
506 |
+
{
|
507 |
+
if (is_array($value))
|
508 |
+
{
|
509 |
+
$a_recursive_diff = $this->array_recursive_diff($value, $array2[$key]);
|
510 |
+
if (count($a_recursive_diff))
|
511 |
+
{
|
512 |
+
$ret[$key] = $a_recursive_diff;
|
513 |
+
}
|
514 |
+
}
|
515 |
+
else
|
516 |
+
{
|
517 |
+
if ((string)$value !== (string)$array2[$key])
|
518 |
+
{
|
519 |
+
$ret[$key] = $value;
|
520 |
+
}
|
521 |
+
}
|
522 |
+
}
|
523 |
+
else
|
524 |
+
{
|
525 |
+
$ret[$key] = $value;
|
526 |
+
}
|
527 |
+
}
|
528 |
+
return $ret;
|
529 |
+
}
|
530 |
+
|
531 |
+
/**
|
532 |
+
* Callback method that handles the Configuration Get event
|
533 |
+
*
|
534 |
+
* @return array
|
535 |
+
*/
|
536 |
+
public function callbackBridgeConfigurationGet()
|
537 |
+
{
|
538 |
+
$magento = array(
|
539 |
+
'adminStub' => (string)Mage::getConfig()->getNode("admin/routers/adminhtml/args")->frontName,
|
540 |
+
'adminUrl' => Mage::helper("adminhtml")->getUrl("adminhtml"),
|
541 |
+
);
|
542 |
+
|
543 |
+
//Order statuses
|
544 |
+
foreach (Mage::getModel('sales/order_status')->getCollection()->joinStates() as $order_status)
|
545 |
+
{
|
546 |
+
$magento['order_statuses'][] = array(
|
547 |
+
'status' => $order_status->getStatus(),
|
548 |
+
'state' => $order_status->getState(),
|
549 |
+
);
|
550 |
+
}
|
551 |
+
|
552 |
+
//Shipping methods
|
553 |
+
$shipping_methods = Mage::getSingleton('shipping/config')->getActiveCarriers();
|
554 |
+
|
555 |
+
foreach ($shipping_methods as $shipping_code => $shipping_model)
|
556 |
+
{
|
557 |
+
$shipping_title = Mage::getStoreConfig('carriers/' . $shipping_code . '/title');
|
558 |
+
$magento['shipping_methods'][] = array(
|
559 |
+
'code' => $shipping_code,
|
560 |
+
'title' => $shipping_title,
|
561 |
+
);
|
562 |
+
}
|
563 |
+
|
564 |
+
//Customer groups
|
565 |
+
foreach (Mage::getModel('customer/group')->getCollection() as $group)
|
566 |
+
{
|
567 |
+
$magento['customerGroups'][] = array(
|
568 |
+
'id' => $group->getId(),
|
569 |
+
'name' => $group->getCode(),
|
570 |
+
);
|
571 |
+
}
|
572 |
+
|
573 |
+
//Tax rates
|
574 |
+
foreach (Mage::getModel('tax/calculation_rate')->getCollection() as $rate)
|
575 |
+
{
|
576 |
+
$magento['tax'][] = array(
|
577 |
+
'code' => $rate->getCode(),
|
578 |
+
'country' => $rate->getCountryId(),
|
579 |
+
'rate' => $rate->getRate(),
|
580 |
+
);
|
581 |
+
}
|
582 |
+
|
583 |
+
$magento['sites'] = $this->getSiteData();
|
584 |
+
|
585 |
+
$modules = (array)Mage::getConfig()->getNode('modules')->children();
|
586 |
+
$core_helper = Mage::helper('core');
|
587 |
+
|
588 |
+
foreach ($modules as $mod_name => $module)
|
589 |
+
{
|
590 |
+
$mod_info = array(
|
591 |
+
'name' => $mod_name,
|
592 |
+
'active' => $module->is('active'),
|
593 |
+
'output' => $core_helper->isModuleOutputEnabled($mod_name),
|
594 |
+
'version' => $module->version,
|
595 |
+
);
|
596 |
+
$magento['modules'][] = $mod_info;
|
597 |
+
}
|
598 |
+
|
599 |
+
$php = array(
|
600 |
+
'version' => PHP_VERSION,
|
601 |
+
'is64bit' => PHP_INT_SIZE === 8,
|
602 |
+
'extensions' => get_loaded_extensions(),
|
603 |
+
'os' => php_uname('a'),
|
604 |
+
'host' => filter_input(INPUT_SERVER, 'HOST') ?: filter_input(INPUT_SERVER, 'HTTP_HOST'),
|
605 |
+
);
|
606 |
+
|
607 |
+
$info = array(
|
608 |
+
'cart' => $magento,
|
609 |
+
'php' => $php, // TODO: add to meta?
|
610 |
+
);
|
611 |
+
|
612 |
+
return $info;
|
613 |
+
}
|
614 |
+
|
615 |
+
private function getSiteData()
|
616 |
+
{
|
617 |
+
$sites = array();
|
618 |
+
|
619 |
+
foreach (Mage::getModel('core/website')->getCollection() as $website)
|
620 |
+
{
|
621 |
+
$sites[$website->getId()] = array(
|
622 |
+
'id' => $website->getId(),
|
623 |
+
'name' => $website->getName(),
|
624 |
+
'defaultStoreId' => $website->getDefaultGroupId(),
|
625 |
+
);
|
626 |
+
}
|
627 |
+
|
628 |
+
foreach (Mage::getModel('core/store_group')->getCollection() as $store_group)
|
629 |
+
{
|
630 |
+
$sites[$store_group->getWebsiteId()]['stores'][$store_group->getGroupId()] = array(
|
631 |
+
'id' => $store_group->getGroupId(),
|
632 |
+
'name' => $store_group->getName(),
|
633 |
+
'rootCategoryId' => $store_group->getRootCategoryId(),
|
634 |
+
'defaultStoreViewId' => $store_group->getDefaultStoreId(),
|
635 |
+
);
|
636 |
+
}
|
637 |
+
|
638 |
+
foreach (Mage::getModel('core/store')->getCollection() as $store_view)
|
639 |
+
{
|
640 |
+
$sites[$store_view->getWebsiteId()]['stores'][$store_view->getGroupId()]['views'][$store_view->getStoreId()] = array(
|
641 |
+
'id' => $store_view->getStoreId(),
|
642 |
+
'name' => $store_view->getName(),
|
643 |
+
'isActive' => (bool)$store_view->getIsActive(),
|
644 |
+
);
|
645 |
+
}
|
646 |
+
|
647 |
+
return array_values($sites);
|
648 |
+
}
|
649 |
+
}
|
app/code/community/Netmatter/Bridge/etc/adminhtml.xml
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<config>
|
3 |
+
<acl>
|
4 |
+
<resources>
|
5 |
+
<admin>
|
6 |
+
<children>
|
7 |
+
<system>
|
8 |
+
<children>
|
9 |
+
<config>
|
10 |
+
<children>
|
11 |
+
<netmatter translate="label" module="bridge">
|
12 |
+
<title>Netmatter</title>
|
13 |
+
<sort_order>9999</sort_order>
|
14 |
+
</netmatter>
|
15 |
+
</children>
|
16 |
+
</config>
|
17 |
+
</children>
|
18 |
+
</system>
|
19 |
+
</children>
|
20 |
+
</admin>
|
21 |
+
</resources>
|
22 |
+
</acl>
|
23 |
+
</config>
|
app/code/community/Netmatter/Bridge/etc/config.xml
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<config>
|
3 |
+
<modules>
|
4 |
+
<Netmatter_Bridge>
|
5 |
+
<version>0.0.1</version>
|
6 |
+
</Netmatter_Bridge>
|
7 |
+
</modules>
|
8 |
+
<global>
|
9 |
+
<helpers>
|
10 |
+
<bridge>
|
11 |
+
<class>Netmatter_Bridge_Helper</class>
|
12 |
+
</bridge>
|
13 |
+
</helpers>
|
14 |
+
<models>
|
15 |
+
<netmatter_bridge>
|
16 |
+
<class>Netmatter_Bridge_Model</class>
|
17 |
+
</netmatter_bridge>
|
18 |
+
</models>
|
19 |
+
<events>
|
20 |
+
<sales_order_place_after>
|
21 |
+
<observers>
|
22 |
+
<netmatter_bridge_order_observer>
|
23 |
+
<type>singleton</type>
|
24 |
+
<class>netmatter_bridge/order_observer</class>
|
25 |
+
<method>sales_order_place_after</method>
|
26 |
+
</netmatter_bridge_order_observer>
|
27 |
+
</observers>
|
28 |
+
</sales_order_place_after>
|
29 |
+
<sales_order_save_after>
|
30 |
+
<observers>
|
31 |
+
<netmatter_bridge_order_observer>
|
32 |
+
<type>singleton</type>
|
33 |
+
<class>netmatter_bridge/order_observer</class>
|
34 |
+
<method>sales_order_save_after</method>
|
35 |
+
</netmatter_bridge_order_observer>
|
36 |
+
</observers>
|
37 |
+
</sales_order_save_after>
|
38 |
+
<sales_order_payment_pay>
|
39 |
+
<observers>
|
40 |
+
<netmatter_bridge_order_observer>
|
41 |
+
<type>singleton</type>
|
42 |
+
<class>netmatter_bridge/order_observer</class>
|
43 |
+
<method>sales_order_payment_pay</method>
|
44 |
+
</netmatter_bridge_order_observer>
|
45 |
+
</observers>
|
46 |
+
</sales_order_payment_pay>
|
47 |
+
</events>
|
48 |
+
<blocks>
|
49 |
+
<bridge>
|
50 |
+
<class>Netmatter_Bridge_Block</class>
|
51 |
+
</bridge>
|
52 |
+
</blocks>
|
53 |
+
</global>
|
54 |
+
<frontend>
|
55 |
+
<routers>
|
56 |
+
<bridge>
|
57 |
+
<use>standard</use>
|
58 |
+
<args>
|
59 |
+
<module>Netmatter_Bridge</module>
|
60 |
+
<frontName>netmatter</frontName>
|
61 |
+
</args>
|
62 |
+
</bridge>
|
63 |
+
</routers>
|
64 |
+
</frontend>
|
65 |
+
</config>
|
app/code/community/Netmatter/Bridge/etc/system.xml
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<config>
|
3 |
+
<tabs>
|
4 |
+
<netmatter translate="label" module="bridge">
|
5 |
+
<label>Netmatter</label>
|
6 |
+
<sort_order>99999</sort_order>
|
7 |
+
</netmatter>
|
8 |
+
</tabs>
|
9 |
+
<sections>
|
10 |
+
<netmatter translate="label" module="bridge">
|
11 |
+
<label>Bridge</label>
|
12 |
+
<tab>netmatter</tab>
|
13 |
+
<frontend_type>text</frontend_type>
|
14 |
+
<sort_order>99</sort_order>
|
15 |
+
<show_in_default>1</show_in_default>
|
16 |
+
<show_in_website>0</show_in_website>
|
17 |
+
<show_in_store>0</show_in_store>
|
18 |
+
<groups>
|
19 |
+
<settings translate="label">
|
20 |
+
<label>Settings</label>
|
21 |
+
<frontend_type>text</frontend_type>
|
22 |
+
<sort_order>1</sort_order>
|
23 |
+
<show_in_default>1</show_in_default>
|
24 |
+
<show_in_website>0</show_in_website>
|
25 |
+
<show_in_store>0</show_in_store>
|
26 |
+
<fields>
|
27 |
+
<enabled translate="label">
|
28 |
+
<label>Enabled</label>
|
29 |
+
<frontend_type>select</frontend_type>
|
30 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
31 |
+
<sort_order>10</sort_order>
|
32 |
+
<show_in_default>1</show_in_default>
|
33 |
+
<show_in_website>0</show_in_website>
|
34 |
+
<show_in_store>0</show_in_store>
|
35 |
+
</enabled>
|
36 |
+
<client_name translate="label">
|
37 |
+
<label>Client Name</label>
|
38 |
+
<frontend_type>text</frontend_type>
|
39 |
+
<sort_order>20</sort_order>
|
40 |
+
<show_in_default>1</show_in_default>
|
41 |
+
<show_in_website>0</show_in_website>
|
42 |
+
<show_in_store>0</show_in_store>
|
43 |
+
</client_name>
|
44 |
+
<source translate="label">
|
45 |
+
<label>Endpoint Key</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>0</show_in_website>
|
50 |
+
<show_in_store>0</show_in_store>
|
51 |
+
</source>
|
52 |
+
<authentication_token translate="label">
|
53 |
+
<label>Authentication Token</label>
|
54 |
+
<frontend_type>text</frontend_type>
|
55 |
+
<sort_order>40</sort_order>
|
56 |
+
<show_in_default>1</show_in_default>
|
57 |
+
<show_in_website>0</show_in_website>
|
58 |
+
<show_in_store>0</show_in_store>
|
59 |
+
</authentication_token>
|
60 |
+
</fields>
|
61 |
+
</settings>
|
62 |
+
</groups>
|
63 |
+
</netmatter>
|
64 |
+
</sections>
|
65 |
+
</config>
|
app/code/community/Netmatter/Bridge/plugin/bridge.php
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if (!defined('NETMATTER_BRIDGE_PLUGIN_ROOT')) { define('NETMATTER_BRIDGE_PLUGIN_ROOT', realpath(dirname(__FILE__))); class Netmatter_Bridge_Registry { private $data = array(); private static $instance = null; private function __construct() {} private static function getInstance() { if (self::$instance !== null) { return self::$instance; } self::$instance = new Netmatter_Bridge_Registry(); return self::$instance; } private function _set($key, $value) { $this->data[$key] = $value; } public static function set($key, $value) { self::getInstance()->_set($key, $value); } private function _get($key) { return isset($this->data[$key]) ? $this->data[$key] : null; } public static function get($key) { return self::getInstance()->_get($key); } private function clearAll() { $this->data = array(); } public static function reset() { self::getInstance()->clearAll(); } } class Netmatter_Bridge_Bridge { private $callbacks = array(); private $connector; private $queue; private $logger; private $host_version; private $integration_version; function __construct(Netmatter_Bridge_Connector $connector, Netmatter_Bridge_Queue $queue, Netmatter_Bridge_Logger_LoggerInterface $logger) { $this->connector = $connector; $this->queue = $queue; $this->logger = $logger; $this->connector->setRetries(0); } public function setCredentials($client_name, $source, $auth) { $this->connector->setClientName($client_name); $this->connector->setSource($source); $this->connector->setAuth($auth); } public function sendOrder(Netmatter_Bridge_DTO_Order $order) { $event = netmatter_bridge_factory_event()->create('order_created'); $event->addOrder($order->toArray()); $meta['version'] = array( 'plugin' => $this->getPluginVersion(), 'integration' => $this->getIntegrationVersion(), 'host' => $this->getHostVersion() ); $event->setMeta($meta, true); return $this->queue->send($event, $this->connector); } public function createOrder() { return netmatter_bridge_factory_dto()->create('order'); } public function registerCallback($event_code, $callback) { $this->callbacks[$event_code] = $callback; return $this; } public function getCallbacks() { return $this->callbacks; } function listen($data = null, array $server = null) { if ($data === null) { $data = file_get_contents('php://input'); } if ($server === null) { $server = $_SERVER; } $this->logger->log('Receiving data: ' . $data); $ret = $this->connector->parseRequest($data, $server); if ($ret->status !== 1) { $this->logger->log('Sending data: ' . $ret->body); foreach ($ret->headers AS $header) header($header); echo $ret->body; return; } $method = 'processEvent' . $ret->code; $output = (array)$this->{$method}($ret->event); $output['meta']['version']['plugin'] = $this->getPluginVersion(); $output['meta']['version']['integration'] = $this->getIntegrationVersion(); $output['meta']['version']['host'] = $this->getHostVersion(); $output = json_encode($output); $this->logger->log('Sending data: ' . $output); header('Content-Type: application/json'); echo $output; } public function processEventProductStockModified(Netmatter_Bridge_Event_ProductStockModified $event) { $errors = array(); foreach ($event->getProducts() AS $product) { $product_key = isset($product['sku']) ? $product['sku'] : $product['id']; ob_start(); try { $this->logger->log('Updating stock for product: ' . $product_key . '. New stock: ' . $product['stock']); $callback_errors = call_user_func($this->callbacks['product_stock_modified'], $product); if ($callback_errors) { $errors[$product_key] = $callback_errors; $this->logger->log('Errors: ' . print_r($callback_errors, true)); } } catch (Exception $e) { $this->logger->log($e); $errors[$product_key] = array('general' => 'Exception: ' . $e->getMessage()); } $unexpected_output = ob_get_clean(); if ($unexpected_output !== '') { $errors[$product_key]['unexpected_output'] = $unexpected_output; } } return array( 'response' => true, 'errors' => $errors ); } public function processEventProductModified(Netmatter_Bridge_Event_ProductModified $event) { $cart_product_ids = array(); $errors = array(); foreach ($event->getProducts() AS $product) { $cart_product_id = null; $product_key = isset($product['sourceId']) ? $product['sourceId'] : $product['identity']['sku']; ob_start(); if (isset($product['prices'])) { foreach ($product['prices'] AS $key => $price) { if (!is_array($price)) { $product['prices'][$key] = array("1" => $price); } } } try { $ret = call_user_func($this->callbacks['product_modified'], $product); if (is_array($ret)) $errors[$product_key] = $ret; else $cart_product_id = $ret; $cart_product_ids[$product_key] = $cart_product_id; if ($cart_product_id !== null) { $this->logger->log('Setting product: ' . $product_key . '. Internal id: ' . $cart_product_id); } } catch (Exception $e) { $this->logger->log($e); $errors[$product_key] = array('general' => 'Exception: ' . $e->getMessage()); } $unexpected_output = ob_get_clean(); if ($unexpected_output !== '') { $errors[$product_key]['unexpected_output'] = $unexpected_output; } } return array( 'response' => $cart_product_ids, 'errors' => $errors ); } public function processEventOrderStatusModified(Netmatter_Bridge_Event_OrderStatusModified $event) { $errors = array(); foreach ($event->getOrders() AS $order) { $order_key = $order['id']; ob_start(); try { $this->logger->log('Updating status for order: ' . $order_key . '. New status: ' . $order['statusId']); $callback_errors = call_user_func($this->callbacks['order_status_modified'], $order); if ($callback_errors) { $errors[$order_key] = $callback_errors; } } catch (Exception $e) { $this->logger->log($e); $errors[$order_key] = array('general' => 'Exception: ' . $e->getMessage()); } $unexpected_output = ob_get_clean(); if ($unexpected_output !== '') { $errors[$order_key]['unexpected_output'] = $unexpected_output; } } return array( 'response' => true, 'errors' => $errors ); } public function processEventConfigurationGet(Netmatter_Bridge_Event_ConfigurationGet $event) { $response = array(); $errors = array(); try { $this->logger->log('Reading configuration'); $response = call_user_func($this->callbacks['configuration_get']); } catch (Exception $e) { $this->logger->log($e); $errors = array('general' => 'Exception: ' . $e->getMessage()); } $unexpected_output = ob_get_clean(); if ($unexpected_output !== '') { $errors['unexpected_output'] = $unexpected_output; } return array( 'response' => $response, 'errors' => $errors ); } public function setHostVersion($host_version) { $this->host_version = $host_version; } public function getHostVersion() { return $this->host_version; } public function setIntegrationVersion($integration_version) { $this->integration_version = $integration_version; } public function getIntegrationVersion() { return $this->integration_version; } public function getPluginVersion() { return NETMATTER_BRIDGE_VERSION; } } class Netmatter_Bridge_Queue { private $queueDir; private $lockHandle; public function __construct($queue_directory) { $this->queueDir = realpath($queue_directory); } public function send(Netmatter_Bridge_Event_AbstractEvent $event, Netmatter_Bridge_Connector $connector) { $queued = (bool)$this->enqueue($event); $this->processQueue($connector); return $queued; } public function enqueue(Netmatter_Bridge_Event_AbstractEvent $event) { $json = json_encode($event->toArray()); list($usec, $sec) = explode(' ', microtime()); $filename = sprintf('%s.%s.json', $sec, substr($usec, 2)); $bytes = file_put_contents($this->queueDir . DIRECTORY_SEPARATOR . $filename, $json); return $bytes === strlen($json); } public function processQueue(Netmatter_Bridge_Connector $connector) { if (!$this->getLock()) { return false; } $sent = 0; while ($filename = $this->getNextFilename()) { $contents = file_get_contents($filename); $event = netmatter_bridge_factory_event()->createFromString($contents); $ret = $connector->sendEvent($event); if (!$ret || !$this->isHttpStatusSuccessful($ret->code)) { break; } $sent++; unlink($filename); } $this->releaseLock(); return $sent; } private function isHttpStatusSuccessful($status_code) { return $status_code >= 200 && $status_code < 300; } private function getLock() { $this->lockHandle = fopen($this->queueDir . '/transmit.lock', 'w'); return flock($this->lockHandle, LOCK_EX|LOCK_NB); } private function getNextFilename() { $queue = glob($this->queueDir . '/*.json'); return isset($queue[0]) ? $queue[0] : false; } private function releaseLock() { if ($this->lockHandle === null) { return; } flock($this->lockHandle, LOCK_UN); } public function getQueueDir() { return $this->queueDir; } public function __destruct() { $this->releaseLock(); } } class Netmatter_Bridge_Connector { private $clientName; private $source; private $auth; private $sslCert; private $retries; private $lastRetries; private $connection; private $logger; public function __construct(Netmatter_Bridge_Connection_ConnectionInterface $connection, Netmatter_Bridge_Logger_LoggerInterface $logger) { $this->connection = $connection; $this->logger = $logger; $this->retries = 0; } public function sendEvent(Netmatter_Bridge_Event_AbstractEvent $event) { $data = $event->toArray(); $data['guid'] = guidv4(); $data['auth'] = $this->getAuth(); $data['source'] = $this->getSource(); $data['clientName'] = $this->getClientName(); $post = json_encode($data); $this->connection->init(); $this->connection->addHeader('Content-Type: application/json'); if ($this->sslCert !== null) { $this->connection->setSSLCert($this->sslCert)->setSSLVerifyPeer(1); } $this->logger->log('Sending data to bridge: ' . $post); $this->lastRetries = 0; while (true) { $response = $this->connection->execute('', $post); $this->logger->log('Receiving from bridge: ' . json_encode($response)); if ($response && $response->code === 202) break; if ($this->lastRetries >= $this->retries) { $this->logger->log('Failed: Maximum retries reached. ' . $this->lastRetries . '. Data: ' . $post); break; } $this->lastRetries++; $this->logger->log('Retrying #' . $this->lastRetries . ': ' . $post); } return $response; } function parseRequest($json, array $server) { $ret = new StdClass(); if (!isset($server['SERVER_PROTOCOL'])) $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; if (!isset($server['REQUEST_METHOD']) || $server['REQUEST_METHOD'] !== 'POST') { $ret->status = 0; $ret->headers = array( $server['SERVER_PROTOCOL'] . ' 405 Method Not Allowed', 'Allow: POST' ); $ret->body = 'Only post requests are allowed'; return $ret; } try { $event = netmatter_bridge_factory_event()->createFromString($json); } catch (Exception $e) { $this->logger->log($e); $ret->status = 0; $ret->headers = array( $server['SERVER_PROTOCOL'] . ' 400 Bad Request' ); $ret->body = json_encode(array('response' => false, 'errors' => array('general' => 'Malformed data given'))); return $ret; } if ($this->checkAuth($event->getAuth()) !== true) { $ret->status = 0; $ret->headers = array( $server['SERVER_PROTOCOL'] . ' 403 Forbidden' ); $ret->body = json_encode(array('response' => false, 'errors' => array('general' => 'Authentication token not valid'))); return $ret; } $ret->status = 1; $ret->event = $event; $ret->code = netmatter_bridge_camelcase($event->getResourceType(), '.') . netmatter_bridge_camelcase($event->getLifecycleEvent()); return $ret; } public function setCredentials($clientName, $source, $auth) { $this->clientName = $clientName; $this->source = $source; $this->auth = $auth; return $this; } function checkAuth($auth) { return $this->auth === $auth; } function getClientName() { return $this->clientName; } function setClientName($clientName) { $this->clientName = $clientName; return $this; } function getSource() { return $this->source; } function setSource($source) { $this->source = $source; return $this; } function getAuth() { return $this->auth; } function setAuth($auth) { $this->auth = $auth; return $this; } function getConnection() { return $this->connection; } function getLogger() { return $this->logger; } function setRetries($retries) { $this->retries = $retries; return $this; } function getRetries() { return $this->retries; } function setSSLCert($sslCert) { $this->sslCert = $sslCert; return $this; } function getSSLCert() { return $this->sslCert; } function getLastRetries() { return $this->lastRetries; } } abstract class Netmatter_Bridge_DTO_AbstractDTO { public function enforceUnicode($string) { if (function_exists('mb_detect_encoding') && mb_detect_encoding($string, array('UTF-8', 'UTF-7', 'ASCII'), true) === false) { $string = utf8_encode($string); } return $string; } abstract public function toArray(); } class Netmatter_Bridge_DTO_Customer extends Netmatter_Bridge_DTO_AbstractDTO { private $id; private $salutation; private $firstname; private $lastname; private $emailAddress; private $telephone; private $mobileTelephone; private $company; private $street; private $suburb; private $city; private $county; private $postcode; private $countryIsoCode; public function setId($id) { $this->id = $id; return $this; } public function getId() { return $this->id; } public function setSalutation($salutation) { $this->salutation = $this->enforceUnicode($salutation); return $this; } public function getSalutation() { return $this->salutation; } public function setFirstname($firstname) { $this->firstname = $this->enforceUnicode($firstname); return $this; } public function getFirstname() { return $this->firstname; } public function setLastname($lastname) { $this->lastname = $this->enforceUnicode($lastname); return $this; } public function geLastname() { return $this->firstname; } public function setEmailAddress($emailAddress) { $this->emailAddress = $this->enforceUnicode($emailAddress); return $this; } public function getEmailAddress() { return $this->emailAddress; } public function setTelephone($telephone) { $this->telephone = $this->enforceUnicode($telephone); return $this; } public function getTelephone() { return $this->telephone; } public function getMobileTelephone() { return $this->mobileTelephone; } public function setMobileTelephone($mobileTelephone) { $this->mobileTelephone = $this->enforceUnicode($mobileTelephone); return $this; } public function setCompany($company) { $this->company = $this->enforceUnicode($company); return $this; } public function getCompany() { return $this->company; } public function setStreet($street) { $this->street = $this->enforceUnicode($street); return $this; } public function getStreet() { return $this->street; } public function setSuburb($suburb) { $this->suburb = $this->enforceUnicode($suburb); return $this; } public function getSuburb() { return $this->suburb; } public function setCity($city) { $this->city = $this->enforceUnicode($city); return $this; } public function getCity() { return $this->city; } public function setCounty($county) { $this->county = $this->enforceUnicode($county); return $this; } public function getCounty() { return $this->county; } public function setPostcode($postcode) { $this->postcode = $this->enforceUnicode($postcode); return $this; } public function getPostcode() { return $this->postcode; } public function setCountryIsoCode($countryIsoCode) { $this->countryIsoCode = $this->enforceUnicode($countryIsoCode); return $this; } public function getCountryIsoCode() { return $this->countryIsoCode; } public function toArray() { return get_object_vars($this); } } class Netmatter_Bridge_DTO_LineItem extends Netmatter_Bridge_DTO_AbstractDTO { private $name; private $options = array(); private $productId; private $sku; private $quantity; private $rowNet; private $rowGross; private $rowTax; private $taxCode; public function setName($name) { $this->name = $this->enforceUnicode($name); return $this; } public function getName() { return $this->name; } public function addOption($key, $value) { $this->options[$key] = $this->enforceUnicode($value); } public function getOptions() { return $this->options; } public function setProductId($productId) { $this->productId = $this->enforceUnicode($productId); return $this; } public function getProductId() { return $this->productId; } public function setSku($sku) { $this->sku = $this->enforceUnicode($sku); return $this; } public function getSku() { return $this->sku; } public function setQuantity($quantity) { $this->quantity = $quantity; return $this; } public function getQuantity() { return $this->quantity; } public function setRowNet($rowNet) { $this->rowNet = $rowNet; return $this; } public function getRowNet() { return $this->rowNet; } public function setRowGross($rowGross) { $this->rowGross = $rowGross; return $this; } public function getRowGross() { return $this->rowGross; } public function setRowTax($rowTax) { $this->rowTax = $rowTax; return $this; } public function getRowTax() { return $this->rowTax; } public function setTaxCode($taxCode) { $this->taxCode = $this->enforceUnicode($taxCode); return $this; } public function getTaxCode() { return $this->taxCode; } public function toArray() { return get_object_vars($this); } } class Netmatter_Bridge_DTO_Address extends Netmatter_Bridge_DTO_AbstractDTO { private $firstname; private $lastname; private $company; private $street; private $suburb; private $city; private $county; private $postcode; private $countryIsoCode; private $telephone; private $mobileTelephone; private $emailAddress; public function setFirstname($firstname) { $this->firstname = $this->enforceUnicode($firstname); return $this; } public function getFirstname() { return $this->firstname; } public function setLastname($lastname) { $this->lastname = $this->enforceUnicode($lastname); return $this; } public function getLastname() { return $this->lastname; } public function setCompany($company) { $this->company = $this->enforceUnicode($company); return $this; } public function getCompany() { return $this->company; } public function setStreet($street) { $this->street = $this->enforceUnicode($street); return $this; } public function getStreet() { return $this->street; } public function setSuburb($suburb) { $this->suburb = $this->enforceUnicode($suburb); return $this; } public function getSuburb() { return $this->suburb; } public function setCity($city) { $this->city = $this->enforceUnicode($city); return $this; } public function getCity() { return $this->city; } public function setCounty($county) { $this->county = $this->enforceUnicode($county); return $this; } public function getCounty() { return $this->county; } public function setPostcode($postcode) { $this->postcode = $this->enforceUnicode($postcode); return $this; } public function getPostcode() { return $this->postcode; } public function setCountryIsoCode($countryIsoCode) { $this->countryIsoCode = $this->enforceUnicode($countryIsoCode); return $this; } public function getCountryIsoCode() { return $this->countryIsoCode; } public function setTelephone($telephone) { $this->telephone = $this->enforceUnicode($telephone); return $this; } public function getTelephone() { return $this->telephone; } public function setMobileTelephone($mobileTelephone) { $this->mobileTelephone = $this->enforceUnicode($mobileTelephone); return $this; } public function getMobileTelephone() { return $this->mobileTelephone; } public function setEmailAddress($emailAddress) { $this->emailAddress = $this->enforceUnicode($emailAddress); return $this; } public function getEmailAddress() { return $this->emailAddress; } public function toArray() { return get_object_vars($this); } } class Netmatter_Bridge_DTO_Shipping extends Netmatter_Bridge_DTO_AbstractDTO { private $method; private $methodLabel; private $net; private $gross; private $tax; private $taxCode; private $gift_message; public function setMethod($method) { $this->method = $this->enforceUnicode($method); return $this; } public function getMethod() { return $this->method; } public function setMethodLabel($methodLabel) { $this->methodLabel = $this->enforceUnicode($methodLabel); return $this; } public function getMethodLabel() { return $this->methodLabel; } public function setNet($net) { $this->net = $net; return $this; } public function getNet() { return $this->net; } public function setGross($gross) { $this->gross = $gross; return $this; } public function getGross() { return $this->gross; } public function setTax($tax) { $this->tax = $tax; return $this; } public function getTax() { return $this->tax; } public function setTaxCode($taxCode) { $this->taxCode = $this->enforceUnicode($taxCode); return $this; } public function getTaxCode() { return $this->taxCode; } public function setGiftMessage($message, $recipient = null, $sender = null) { $this->gift_message = array( 'message' => $message, 'recipient' => $recipient, 'sender' => $sender ); return $this; } public function getGiftMessage() { return $this->gift_message; } public function toArray() { return get_object_vars($this); } } class Netmatter_Bridge_DTO_Discount extends Netmatter_Bridge_DTO_AbstractDTO { private $label; private $taxCode; private $net; private $gross; private $tax; public function setLabel($label) { $this->label = $this->enforceUnicode($label); return $this; } public function getLabel() { return $this->label; } public function setTaxCode($taxCode) { $this->taxCode = $this->enforceUnicode($taxCode); return $this; } public function getTaxCode() { return $this->taxCode; } public function setNet($net) { $this->net = $net; return $this; } public function getNet() { return $this->net; } public function setGross($gross) { $this->gross = $gross; return $this; } public function getGross() { return $this->gross; } public function setTax($tax) { $this->tax = $tax; return $this; } public function getTax() { return $this->tax; } public function toArray() { return get_object_vars($this); } } class Netmatter_Bridge_DTO_Payment extends Netmatter_Bridge_DTO_AbstractDTO { private $method; private $currency; private $baseCurrency; private $amount; private $baseAmount; private $notes = array(); private $exceptions = array(); private $details; public function setMethod($method) { $this->method = $this->enforceUnicode($method); return $this; } public function getMethod() { return $this->method; } public function setCurrency($currency) { $this->currency = $this->enforceUnicode($currency); return $this; } public function getCurrency() { return $this->currency; } public function setBaseCurrency($baseCurrency) { $this->baseCurrency = $this->enforceUnicode($baseCurrency); return $this; } public function getBaseCurrency() { return $this->baseCurrency; } public function setAmount($amount) { $this->amount = $amount; return $this; } public function getAmount() { return $this->amount; } public function setBaseAmount($baseAmount) { $this->baseAmount = $baseAmount; return $this; } public function getBaseAmount() { return $this->baseAmount; } public function addNote($note) { $this->notes[] = $this->enforceUnicode($note); return $this; } public function getNotes() { return $this->notes; } public function addException($exception) { $this->exceptions[] = $this->enforceUnicode($exception); return $this; } public function getExceptions() { return $this->exceptions; } public function addSagepayDetails() { $this->details = new Netmatter_Bridge_DTO_PaymentDetails_Sagepay(); return $this->details; } public function addDefaultDetails() { $this->details = new Netmatter_Bridge_DTO_PaymentDetails_Default(); return $this->details; } public function addInfusionSoftDetails() { $this->details = new Netmatter_Bridge_DTO_PaymentDetails_InfusionSoft(); return $this->details; } public function addPaypalDetails() { $this->details = new Netmatter_Bridge_DTO_PaymentDetails_Paypal(); return $this->details; } public function addEpdqDetails() { $this->details = new Netmatter_Bridge_DTO_PaymentDetails_Epdq(); return $this->details; } public function addWorldPayDetails() { $this->details = new Netmatter_Bridge_DTO_PaymentDetails_WorldPay(); return $this->details; } public function toArray() { $data = get_object_vars($this); if ($this->details !== null) $data['details'] = $this->details->toArray(); return $data; } } abstract class Netmatter_Bridge_DTO_PaymentDetails_AbstractPaymentDetails extends Netmatter_Bridge_DTO_AbstractDTO { protected $txId; protected $status; protected $statusLabel; public function setTxId($txId) { $this->txId = $this->enforceUnicode($txId); return $this; } public function getTxId() { return $this->txId; } public function setStatus($status) { $this->status = $this->enforceUnicode($status); return $this; } public function getStatus() { return $this->status; } public function setStatusLabel($statusLabel) { $this->statusLabel = $this->enforceUnicode($statusLabel); return $this; } public function getStatusLabel() { return $this->statusLabel; } } class Netmatter_Bridge_DTO_PaymentDetails_Default extends Netmatter_Bridge_DTO_PaymentDetails_AbstractPaymentDetails { private $values = array(); public function setValue($key, $value) { $this->values[$key] = $this->enforceUnicode($value); return $this; } public function getValue($key) { if (!isset($this->values[$key])) { return null; } return $this->values[$key]; } public function toArray() { $ret = get_object_vars($this); unset($ret['values']); foreach ($this->values as $key => $value) { $ret[$key] = $value; } return $ret; } } class Netmatter_Bridge_DTO_PaymentDetails_Paypal extends Netmatter_Bridge_DTO_PaymentDetails_AbstractPaymentDetails { private $payerId; private $payerFirstname; private $payerLastname; private $payerEmailAddress; private $type; public function setPayerId($payerId) { $this->payerId = $this->enforceUnicode($payerId); return $this; } public function getPayerId() { return $this->payerId; } public function setPayerFirstname($payerFirstname) { $this->payerFirstname = $this->enforceUnicode($payerFirstname); return $this; } public function getPayerFirstname() { return $this->payerFirstname; } public function setPayerLastname($payerLastname) { $this->payerLastname = $this->enforceUnicode($payerLastname); return $this; } public function getPayerLastname() { return $this->payerLastname; } public function setPayerEmailAddress($payerEmailAddress) { $this->payerEmailAddress = $this->enforceUnicode($payerEmailAddress); return $this; } public function getPayerEmailAddress() { return $this->payerEmailAddress; } public function setType($type) { $this->type = $this->enforceUnicode($type); return $this; } public function getType() { return $this->type; } public function toArray() { return get_object_vars($this); } } class Netmatter_Bridge_DTO_PaymentDetails_Epdq extends Netmatter_Bridge_DTO_PaymentDetails_AbstractPaymentDetails { private $ccBrand; private $aavCheck; private $cvcCheck; public function setCcBrand($ccBrand) { $this->ccBrand = $this->enforceUnicode($ccBrand); return $this; } public function getCcBrand() { return $this->ccBrand; } public function setAavCheck($aavCheck) { $this->aavCheck = $this->enforceUnicode($aavCheck); return $this; } public function getAavCheck() { return $this->aavCheck; } public function setCvcCheck($cvcCheck) { $this->cvcCheck = $this->enforceUnicode($cvcCheck); return $this; } public function getCvcCheck() { return $this->cvcCheck; } public function toArray() { return get_object_vars($this); } } class Netmatter_Bridge_DTO_PaymentDetails_Sagepay extends Netmatter_Bridge_DTO_PaymentDetails_AbstractPaymentDetails { private $authCode; private $avsCv2Check; private $cv2Result; private $addressResult; private $postcodeResult; private $threeDSecureStatus; public function setAuthCode($authCode) { $this->authCode = $this->enforceUnicode($authCode); return $this; } public function getAuthCode() { return $this->authCode; } public function setAvsCv2Check($avsCv2Check) { $this->avsCv2Check = $this->enforceUnicode($avsCv2Check); return $this; } public function getAvsCv2Check() { return $this->avsCv2Check; } public function setCv2Result($cv2Result) { $this->cv2Result = $this->enforceUnicode($cv2Result); return $this; } public function getCv2Result() { return $this->cv2Result; } public function setAddressResult($addressResult) { $this->addressResult = $this->enforceUnicode($addressResult); return $this; } public function getAddressResult() { return $this->addressResult; } public function setPostcodeResult($postcodeResult) { $this->postcodeResult = $this->enforceUnicode($postcodeResult); return $this; } public function getPostcodeResult() { return $this->postcodeResult; } public function setThreeDSecureStatus($threeDSecureStatus) { $this->threeDSecureStatus = $this->enforceUnicode($threeDSecureStatus); return $this; } public function getThreedSecureStatus() { return $this->threeDSecureStatus; } public function toArray() { return get_object_vars($this); } } class Netmatter_Bridge_DTO_PaymentDetails_WorldPay extends Netmatter_Bridge_DTO_PaymentDetails_AbstractPaymentDetails { private $payerName; private $payerEmailAddress; private $message; private $transStatus; private $countryMatch; private $avs; private $rawAuthCode; private $authMode; public function setPayerName($payerName) { $this->payerName = $this->enforceUnicode($payerName); return $this; } public function getPayerName() { return $this->payerName; } public function setPayerEmailAddress($payerEmailAddress) { $this->payerEmailAddress = $this->enforceUnicode($payerEmailAddress); return $this; } public function getPayerEmailAddress() { return $this->payerEmailAddress; } public function setMessage($message) { $this->message = $this->enforceUnicode($message); return $this; } public function getMessage() { return $this->message; } public function setCountryMatch($countryMatch) { $this->countryMatch = $this->enforceUnicode($countryMatch); return $this; } public function getCountryMatch() { return $this->countryMatch; } public function setAvs($avs) { $this->avs = $this->enforceUnicode($avs); return $this; } public function getAvs() { return $this->avs; } public function setRawAuthCode($rawAuthCode) { $this->rawAuthCode = $this->enforceUnicode($rawAuthCode); return $this; } public function getRawAuthCode() { return $this->rawAuthCode; } public function setAuthmode($authMode) { $this->authMode = $this->enforceUnicode($authMode); return $this; } public function getAuthMode() { return $this->authMode; } public function toArray() { return get_object_vars($this); } } class Netmatter_Bridge_DTO_PaymentDetails_InfusionSoft extends Netmatter_Bridge_DTO_PaymentDetails_AbstractPaymentDetails { public function toArray() { return get_object_vars($this); } } class Netmatter_Bridge_DTO_Order extends Netmatter_Bridge_DTO_AbstractDTO { private $id; private $publicId; private $comment; private $channelId; private $lineItems = array(); private $customer; private $delivery; private $billing; private $shipping; private $discounts = array(); private $payment; private $leadSource; private $datePlaced; private $orderStatus; private $total; private $isPaid; public function setId($id) { $this->id = $id; return $this; } public function getId() { return $this->id; } public function setPublicId($publicId) { $this->publicId = $publicId; return $this; } public function getPublicId() { return $this->publicId; } function addLineItem() { $lineItem = new Netmatter_Bridge_DTO_LineItem(); $this->lineItems[] = $lineItem; return $lineItem; } public function getLineItems() { return $this->lineItems; } public function addCustomer() { $this->customer = new Netmatter_Bridge_DTO_Customer(); return $this->customer; } public function getCustomer() { return $this->customer; } public function addDelivery() { $this->delivery = new Netmatter_Bridge_DTO_Address(); return $this->delivery; } public function getDelivery() { return $this->delivery; } public function addBilling() { $this->billing = new Netmatter_Bridge_DTO_Address(); return $this->billing; } public function getBilling() { return $this->billing; } public function setComment($comment) { $this->comment = $this->enforceUnicode($comment); return $this; } public function getComment() { return $this->comment; } public function setChannelId($channelId) { $this->channelId = $this->enforceUnicode($channelId); return $this; } public function getChannelId() { return $this->channelId; } public function setOrderStatus($orderStatus) { $this->orderStatus = $this->enforceUnicode($orderStatus); return $this; } public function getOrderStatus() { return $this->orderStatus; } public function setLeadSource($leadSource) { $this->leadSource = $this->enforceUnicode($leadSource); return $this; } public function getLeadSource() { return $this->leadSource; } public function setDatePlaced($datePlaced) { $this->datePlaced = $datePlaced; return $this; } public function getDatePlaced() { return $this->datePlaced; } public function getTotal() { return $this->total; } public function setTotal($total) { $this->total = $total; return $this; } public function setIsPaid($isPaid) { $this->isPaid = $isPaid; return $this; } public function getIsPaid() { return $this->isPaid; } public function addShipping() { $this->shipping = new Netmatter_Bridge_DTO_Shipping(); return $this->shipping; } public function getShipping() { return $this->shipping; } public function addPayment() { $this->payment = new Netmatter_Bridge_DTO_Payment(); return $this->payment; } public function addDiscount() { $discount = new Netmatter_Bridge_DTO_Discount(); $this->discounts[] = $discount; return $discount; } public function getDiscounts() { return $this->discounts; } public function getPayment() { return $this->payment; } public function toArray() { $data = get_object_vars($this); if ($this->customer !== null) $data['customer'] = $this->customer->toArray(); if ($this->delivery !== null) $data['delivery'] = $this->delivery->toArray(); if ($this->billing !== null) $data['billing'] = $this->billing->toArray(); if ($this->shipping !== null) $data['shipping'] = $this->shipping->toArray(); if ($this->payment !== null) $data['payment'] = $this->payment->toArray(); $data['discounts'] = array(); foreach ($this->discounts AS $key => $discount) $data['discounts'][$key] = $discount->toArray(); $data['lineItems'] = array(); foreach ($this->lineItems AS $key => $lineItem) $data['lineItems'][$key] = $lineItem->toArray(); return $data; } } abstract class Netmatter_Bridge_Event_AbstractEvent { protected $auth; protected $idSet; protected $client_name; protected $source_name; protected $meta = array(); abstract public function parseObjects(array $objects); public function setAuth($auth) { $this->auth = $auth; } public function getAuth() { return $this->auth; } abstract public function getResourceType(); public function setIdSet($idSet) { $this->idSet = $idSet; } public function getIdSet() { return $this->idSet; } public function setMeta(array $meta, $append = false) { if ($append !== false) $this->meta = array_merge($meta, $this->meta); else $this->meta = $meta; } public function getMeta() { return $this->meta; } abstract public function getLifecycleEvent(); abstract public function getObjects(); public function toArray() { return array( 'auth' => $this->auth, 'resourceType' => $this->getResourceType(), 'idSet' => $this->idSet, 'lifecycleEvent' => $this->getLifecycleEvent(), 'objects' => $this->getObjects(), 'meta' => $this->getMeta() ); } public function toJson() { return json_encode($this->toArray()); } } class Netmatter_Bridge_Event_OrderCreated extends Netmatter_Bridge_Event_AbstractEvent { private $orders = array(); public function getResourceType() { return 'order'; } public function getLifecycleEvent() { return 'created'; } public function addOrder(array $data) { $errors = $this->checkOrder($data); if ($errors) return $errors; $this->orders[] = $data; $this->syncIdSet(); } public function checkOrder(array $data) { return array(); } private function syncIdSet() { $ids = array(); foreach ($this->orders AS $order) $ids[] = $order['id']; $this->setIdSet(implode(',', $ids)); } public function parseObjects(array $objects) { if (!isset($objects['orders'])) throw new Exception('Orders key not present'); $orders = array(); foreach ($objects['orders'] AS $key => $order) { $errors = $this->checkOrder($order); if ($errors) return $errors; $orders[] = $order; } $this->orders = $orders; } public function getOrders() { return $this->orders; } public function getObjects() { return array( 'orders' => $this->orders ); } } class Netmatter_Bridge_Event_OrderStatusModified extends Netmatter_Bridge_Event_AbstractEvent { private $orders = array(); public function getResourceType() { return 'order.status'; } public function getLifecycleEvent() { return 'modified'; } public function parseObjects(array $objects) { if (!isset($objects['orders'])) throw new Exception('Orders key not present'); $orders = array(); foreach ($objects['orders'] AS $key => $order) { if (!isset($order['id'])) throw new Exception('Order id not set'); if (!isset($order['statusId'])) throw new Exception('Order statusId not set'); $orders[] = $order; } $this->orders = $orders; } public function getOrders() { return $this->orders; } public function getObjects() { return array( 'order' => $this->orders ); } } class Netmatter_Bridge_Event_ProductCreated extends Netmatter_Bridge_Event_AbstractEvent { private $products = array(); public function getResourceType() { return 'product'; } public function getLifecycleEvent() { return 'created'; } public function parseObjects(array $objects) { if (!isset($objects['products'])) throw new Exception('Products key not present'); $products = array(); foreach ($objects['products'] AS $key => $product) { $products[] = $product; } $this->products = $products; } public function getProducts() { return $this->products; } public function getObjects() { return array( 'products' => $this->products ); } } class Netmatter_Bridge_Event_ProductModified extends Netmatter_Bridge_Event_AbstractEvent { private $products = array(); public function getResourceType() { return 'product'; } public function getLifecycleEvent() { return 'modified'; } public function parseObjects(array $objects) { if (!isset($objects['products'])) throw new Exception('Products key not present'); $products = array(); foreach ($objects['products'] AS $key => $product) { if (!isset($product['identity'])) { throw new Exception('Missing products informations'); } $products[] = $product; } $this->products = $products; } public function getProducts() { return $this->products; } public function getObjects() { return array( 'products' => $this->products ); } } class Netmatter_Bridge_Event_ProductStockModified extends Netmatter_Bridge_Event_AbstractEvent { private $products = array(); public function getResourceType() { return 'product.stock'; } public function getLifecycleEvent() { return 'modified'; } public function parseObjects(array $objects) { if (!isset($objects['products'])) throw new Exception('Products key not present'); $products = array(); foreach ($objects['products'] AS $key => $product) { if (!isset($product['id']) && !isset($product['sku'])) throw new Exception('Product id or sku not set'); if (!isset($product['stock'])) throw new Exception('Product stock not set or invalid'); $products[] = $product; } $this->products = $products; } public function getProducts() { return $this->products; } public function getObjects() { return array( 'products' => $this->products ); } } class Netmatter_Bridge_Event_ConfigurationGet extends Netmatter_Bridge_Event_AbstractEvent { public function getResourceType() { return 'configuration'; } public function getLifecycleEvent() { return 'get'; } public function parseObjects(array $objects) { } public function getObjects() { return array(); } } class Netmatter_Bridge_Factory_DTO { public function create($key) { $key = netmatter_bridge_camelcase($key); $class_name = 'Netmatter_Bridge_DTO_' . $key; return new $class_name(); } } class Netmatter_Bridge_Factory_Event { public function create($key) { $key = netmatter_bridge_camelcase($key); $class_name = 'Netmatter_Bridge_Event_' . $key; return new $class_name(); } public function createFromString($string) { $data = json_decode($string, true); if ($data === null) { throw new Exception('Invalid json string given'); } if (!isset($data['resourceType']{0})) { throw new Exception('ResourceType not present'); } if (!isset($data['lifecycleEvent']{0})) { throw new Exception('LivecycleEvent not present'); } $key = str_replace('.', '_', $data['resourceType']) . '_' . $data['lifecycleEvent']; $event = $this->create($key); if (isset($data['auth']{0})) { $event->setAuth($data['auth']); } if (isset($data['idSet']{0})) { $event->setIdSet($data['idSet']); } if (isset($data['objects'])) { $event->parseObjects($data['objects']); } if (isset($data['meta'])) { $event->setMeta($data['meta']); } return $event; } } interface Netmatter_Bridge_Logger_LoggerInterface { public function log($text); } class Netmatter_Bridge_Logger_Array implements Netmatter_Bridge_Logger_LoggerInterface { private $logs = array(); public function log($text) { $this->logs[] = $text; } public function getLogs() { return $this->logs; } public function getLast() { $count = count($this->logs); return $count > 0 ? $this->logs[$count - 1] : null; } } class Netmatter_Bridge_Logger_File implements Netmatter_Bridge_Logger_LoggerInterface { private $file; public function __construct($file) { $this->file = $file; } public function log($text) { $handler = fopen($this->file, 'a+'); fwrite($handler, gmdate('d.m.Y H:i:s T', time()) . ' | ' . $text . PHP_EOL . PHP_EOL); fclose($handler); } } class Netmatter_Bridge_Logger_RotatingFiles implements Netmatter_Bridge_Logger_LoggerInterface { private $dir; private $chanceOfCleanup = 1000; private $numberOfOldLogs = 6; private $uniqueId; public function __construct($dir) { $this->dir = $dir; $this->file = $this->createLogName(); $this->uniqueId = uniqid(getmypid()); } private function createLogName($time = null) { if (!$time) { $time = time(); } return date('Y-m-d', $time) . '.log'; } public function log($text) { $handler = fopen($this->dir . '/' . $this->file, 'a+'); fwrite($handler, gmdate('Y-m-d\TH:i:s\Z') . ' @' . $this->uniqueId . ' ' . $text . PHP_EOL); fclose($handler); } public function clearOldLogs() { $first_log = $this->createLogName(strtotime($this->numberOfOldLogs . ' days ago')); foreach (glob($this->dir . '/????-??-??.log') as $filename) { if (basename($filename) < $first_log) { unlink($filename); } } } public function __destruct() { if (mt_rand(0, $this->chanceOfCleanup) === 0) { $this->clearOldLogs(); } } } class Netmatter_Bridge_Logger_Null implements Netmatter_Bridge_Logger_LoggerInterface { public function log($text) { } } class Netmatter_Bridge_Logger_Screen implements Netmatter_Bridge_Logger_LoggerInterface { public function log($text) { echo $text . PHP_EOL; } } interface Netmatter_Bridge_Connection_ConnectionInterface { public function init(); public function close(); public function addHeader($header); public function getHeaders(); public function setTimeout($timeout); public function setSSLCert($ssl_cert); public function getSSLCert(); public function setSSLVerifyPeer($ssl_verify_peer); public function getSSLVerifyPeer(); public function execute($query_string = null, $post_string = null); } class Netmatter_Bridge_Connection_Curl implements Netmatter_Bridge_Connection_ConnectionInterface { private $url; private $handler; private $headers = array(); private $options = array(); function __construct($url) { $this->url = $url; } function init() { $this->close(); $this->handler = curl_init($this->url); $this->options = array(); $this->headers = array(); $this->setOption(CURLOPT_RETURNTRANSFER, 1); $this->setOption(CURLOPT_CONNECTTIMEOUT, 3); $this->setOption(CURLOPT_HEADER, 1); $this->setOption(CURLOPT_TIMEOUT, 30); $this->setOption(CURLOPT_SSL_VERIFYHOST, 2); $this->setOption(CURLOPT_SSL_VERIFYPEER, 0); } function close() { if ($this->handler !== null) { curl_close($this->handler); $this->handler = null; } } public function getUrl() { return $this->url; } public function addHeader($header) { if (!in_array($header, $this->headers)) $this->headers[] = $header; return $this; } public function getHeaders() { return $this->headers; } public function setTimeout($timeout) { $this->setOption(CURLOPT_TIMEOUT, $timeout); } public function setSSLCert($ssl_cert) { $this->setOption(CURLOPT_SSLCERT, $ssl_cert); return $this; } public function getSSLCert() { return $this->getOption(CURLOPT_SSLCERT); } public function setSSLVerifyPeer($ssl_verify_peer) { $this->setOption(CURLOPT_SSL_VERIFYPEER, $ssl_verify_peer); return $this; } public function getSSLVerifyPeer() { return $this->getOption(CURLOPT_SSL_VERIFYPEER); } public function setOption($key, $value) { $this->options[$key] = $value; return $this; } public function getOption($key) { return isset($this->options[$key]) ? $this->options[$key] : null; } public function getOptions() { return $this->options; } function curlExec() { return curl_exec($this->handler); } function curlGetInfo($opt) { return curl_getinfo($this->handler, $opt); } function execute($query_string = null, $post_string = null) { if ($this->handler === null) $this->init(); if ($query_string !== null) { $this->setOption(CURLOPT_URL, $this->url . $query_string); } if ($post_string !== null) { $this->setOption(CURLOPT_POST, 1); $this->setOption(CURLOPT_POSTFIELDS, $post_string); } if (count($this->headers) > 0) { $this->setOption(CURLOPT_HTTPHEADER, $this->headers); } foreach ($this->options AS $key => $value) curl_setopt($this->handler, $key, $value); $response = $this->curlExec($this->handler); if ($response === false) { return (object)array('code' => 0, 'headers' => array(), 'body' => 'Error: ' . curl_error($this->handler) . '. No: ' . curl_errno($this->handler)); } $code = $this->curlGetInfo(CURLINFO_HTTP_CODE); $header_size = $this->curlGetInfo(CURLINFO_HEADER_SIZE); $header = substr($response, 0, $header_size); $headers = netmatter_bridge_parse_http_headers($header); $body = substr($response, $header_size); return (object)array('code' => $code, 'headers' => $headers, 'body' => $body); } function getHandler() { return $this->handler; } function __destruct() { $this->close(); } } function netmatter_bridge_connector() { $connector = Netmatter_Bridge_Registry::get('service::connector'); if ($connector !== null) { return $connector; } $connection = new Netmatter_Bridge_Connection_Curl(Netmatter_Bridge_Registry::get('config::bridge_callback')); $connector = new Netmatter_Bridge_Connector($connection, netmatter_bridge_logger()); $connector->setAuth(Netmatter_Bridge_Registry::get('config::auth')); $connector->setClientName(Netmatter_Bridge_Registry::get('config::client_name')); $connector->setSource(Netmatter_Bridge_Registry::get('config::source')); Netmatter_Bridge_Registry::set('service::connector', $connector); return $connector; } function netmatter_bridge_bridge() { $bridge = Netmatter_Bridge_Registry::get('service::bridge'); if ($bridge !== null) { return $bridge; } $bridge = new Netmatter_Bridge_Bridge(netmatter_bridge_connector(), netmatter_bridge_queue(), netmatter_bridge_logger()); Netmatter_Bridge_Registry::set('service::bridge', $bridge); return $bridge; } function netmatter_bridge_logger() { $logger = null; $log_type = Netmatter_Bridge_Registry::get('config::log_type'); switch ($log_type) { case 'file': $log_file = Netmatter_Bridge_Registry::get('config::log_file'); if (!isset($log_file{0})) throw new Exception('Netmatter bridge log_file not set or not writable'); $logger = new Netmatter_Bridge_Logger_File($log_file); break; case 'files': $log_dir = Netmatter_Bridge_Registry::get('config::log_dir'); if (!isset($log_dir)) { throw new Exception('Netmatter bridge log_dir not set'); } $logger = new Netmatter_Bridge_Logger_RotatingFiles($log_dir); break; case 'array': $logger = new Netmatter_Bridge_Logger_Array(); break; case 'screen': $logger = new Netmatter_Bridge_Logger_Screen(); break; default: $logger = new Netmatter_Bridge_Logger_Null(); } return $logger; } function netmatter_bridge_queue() { $queue = Netmatter_Bridge_Registry::get('service::queue'); if ($queue !== null) { return $queue; } $queue_directory = Netmatter_Bridge_Registry::get('config::queue_dir'); $queue = new Netmatter_Bridge_Queue($queue_directory); Netmatter_Bridge_Registry::set('service::queue', $queue); return $queue; } function netmatter_bridge_factory_event() { return new Netmatter_Bridge_Factory_Event(); } function netmatter_bridge_factory_dto() { return new Netmatter_Bridge_Factory_DTO(); } function netmatter_bridge_camelcase($string, $separator = null) { if ($separator === null) { $separator = '_'; } return str_replace(' ', '', ucwords(str_replace($separator, ' ', $string))); } function netmatter_bridge_parse_http_headers($header) { $headers = array(); $key = ''; foreach(explode("\n", $header) AS $i => $h) { $h = explode(':', $h, 2); if (isset($h[1])) { if (!isset($headers[$h[0]])) { $headers[$h[0]] = trim($h[1]); } elseif (is_array($headers[$h[0]])) { $headers[$h[0]] = array_merge($headers[$h[0]], array(trim($h[1]))); } else { $headers[$h[0]] = array_merge(array($headers[$h[0]]), array(trim($h[1]))); } $key = $h[0]; } else { if (substr($h[0], 0, 1) == "\t") { $headers[$key] .= "\r\n\t" . trim($h[0]); } elseif (!$key) { $headers[0] = trim($h[0]); } } } return $headers; } function guidv4() { if (function_exists('openssl_random_pseudo_bytes')) { $data = openssl_random_pseudo_bytes(16); $data[6] = chr(ord($data[6]) & 0x0f | 0x40); $data[8] = chr(ord($data[8]) & 0x3f | 0x80); return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); } else { return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) ); } } function netmatter_float_equals($float1, $float2) { if (function_exists('bccomp')) { return bccomp($float1, $float2, 10) === 0; } $epsilon = 0.0000000001; return abs($float1 - $float2) < $epsilon; } if (is_readable(NETMATTER_BRIDGE_PLUGIN_ROOT . '/config.php')) { require(NETMATTER_BRIDGE_PLUGIN_ROOT . '/config.php'); } if (isset($netmatter_bridge_config) && is_array($netmatter_bridge_config)) { foreach ($netmatter_bridge_config AS $key => $value) { Netmatter_Bridge_Registry::set('config::' . $key, $value); } } $bridge = netmatter_bridge_bridge(); define('NETMATTER_BRIDGE_VERSION', '1.2.2'); } $bridge = netmatter_bridge_bridge();
|
app/code/community/Netmatter/Bridge/plugin/config.php
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
//The url that the connector will try to communicate to
|
3 |
+
$netmatter_bridge_config['bridge_callback'] = 'https://test/callback.php';
|
4 |
+
|
5 |
+
//Queue settings
|
6 |
+
$netmatter_bridge_config['queue_dir'] = Mage::getBaseDir('var') . '/bridge/queue'; // Outgoing message queue directory
|
7 |
+
|
8 |
+
//The log settings
|
9 |
+
$netmatter_bridge_config['log_type'] = 'files'; //screen|array|file|files|null
|
10 |
+
$netmatter_bridge_config['log_file'] = ''; //When logger is file, the filename to write logs to
|
11 |
+
$netmatter_bridge_config['log_dir'] = Mage::getBaseDir('var') . '/bridge/log'; //When logger is RotatingFiles, the dir to use
|
app/etc/modules/Netmatter_Bridge.xml
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<!--
|
3 |
+
/**
|
4 |
+
* Netmatter
|
5 |
+
*
|
6 |
+
* @category Netmatter
|
7 |
+
* @package Netmatter_Bridge
|
8 |
+
* @author Netmatter Team
|
9 |
+
* @copyright Copyright (c) 2008-2014 Netmatter Ltd. (http://www.netmatter.co.uk)
|
10 |
+
*/
|
11 |
+
-->
|
12 |
+
<config>
|
13 |
+
<modules>
|
14 |
+
<Netmatter_Bridge>
|
15 |
+
<active>true</active>
|
16 |
+
<codePool>community</codePool>
|
17 |
+
</Netmatter_Bridge>
|
18 |
+
</modules>
|
19 |
+
</config>
|
package.xml
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<package>
|
3 |
+
<name>Netmatter_Bridge</name>
|
4 |
+
<version>1.1.0</version>
|
5 |
+
<stability>stable</stability>
|
6 |
+
<license uri="http://www.opensource.org/licenses/osl-3.0.php">OSL v3.0</license>
|
7 |
+
<channel>community</channel>
|
8 |
+
<extends/>
|
9 |
+
<summary>The Volo extension to Magento, powered by Netmatter, allows all high performance Magento stores – from single instance up to multiple, multi-lingual stores – to carry out 2-way bulk data transfers with the Volo platform in real time, with no loss of Magento performance.</summary>
|
10 |
+
<description>Volo Commerce’s platform and services orchestrate the complex ecommerce universe for large online merchants and brands to help them quickly and sustainably 
|
11 |
+
grow their multichannel sales.
|
12 |
+

|
13 |
+
At the heart of this is a comprehensive platform which manages multichannel ecommerce in all the key areas: purchasing, supply, inventory, stock levels, orders, fulfilment, shipping, refunds and returns, and customer service.
|
14 |
+

|
15 |
+
The platform automates what were previously manual processes, allowing sellers to grow rapidly and realise major productivity gains which impact both top line revenues and bottom line profits. 
|
16 |
+

|
17 |
+
In the area of multichannel management, the Volo platform enables sellers to add new channels quickly, centrally manage all their channels and post optimised listings simultaneously across the different marketplaces where they have a presence. All Volo customers on average double their revenues within 24 months.
|
18 |
+

|
19 |
+
The Volo extension to Magento, powered by Netmatter, allows all high performance Magento stores – from single instance up to multiple, multi-lingual stores – to carry out 2-way bulk data transfers with the Volo platform in real time, with no loss of Magento performance.
|
20 |
+

|
21 |
+
This official extension is part of the Netmatter iPaaS network, which makes it possible for multichannel sellers to connect their store to a network of integrated end-points through one common language and configuration.
|
22 |
+
</description>
|
23 |
+
<notes>The Volo extension to Magento, powered by Netmatter, allows all high performance Magento stores – from single instance up to multiple, multi-lingual stores – to carry out 2-way bulk data transfers with the Volo platform in real time, with no loss of Magento performance.</notes>
|
24 |
+
<authors><author><name>Netmatter</name><user>netmatter</user><email>support@netmatter.co.uk</email></author></authors>
|
25 |
+
<date>2016-05-25</date>
|
26 |
+
<time>15:57:36</time>
|
27 |
+
<contents><target name="magecommunity"><dir name="Netmatter"><dir name="Bridge"><dir name="Block"><dir name="Adminhtml"><file name="Config.php" hash="c591e8ab43b0ec3573eca83b87daba54"/></dir></dir><dir name="Helper"><file name="Data.php" hash="58598d341eb1190cbd46771510d60c34"/></dir><dir name="Model"><dir name="Order"><file name="Observer.php" hash="2e721431bab206c7c85d9a300f3abfc2"/></dir></dir><dir name="controllers"><file name="CallbackController.php" hash="ac557ce846b284a6c0ba75cfbad85b8b"/></dir><dir name="etc"><file name="adminhtml.xml" hash="182a9db254fd689ee893e2ee5f99de07"/><file name="config.xml" hash="c22bd996078f42df236720d9949148b7"/><file name="system.xml" hash="6d0d3b24fa437f789ee41109773c7440"/></dir><dir name="plugin"><file name="bridge.php" hash="f9140bb29b8c63765fbfdd59018d10f2"/><file name="config.php" hash="9767abf32066dbec049a6eac028ae02c"/></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Netmatter_Bridge.xml" hash="2183bab433800ec9839bb4866e96fc78"/></dir></target><target name="mage"><dir name="var"><dir name="bridge"><dir name="log"><file name="warning.txt" hash="40dcbdea0a509204682ce3e57d3b9487"/></dir><dir name="queue"><file name="warning.txt" hash="40dcbdea0a509204682ce3e57d3b9487"/></dir></dir></dir></target></contents>
|
28 |
+
<compatible/>
|
29 |
+
<dependencies><required><php><min>5.2.2</min><max>7.0.0</max></php></required></dependencies>
|
30 |
+
</package>
|
var/bridge/log/warning.txt
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
The logger will assume that any file in this directory is safe to delete.
|
var/bridge/queue/warning.txt
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
The logger will assume that any file in this directory is safe to delete.
|