From 18aaac30cdb4788d1749f03ca5129fc3b508e55f Mon Sep 17 00:00:00 2001 From: Fanis Strezos Date: Thu, 21 Feb 2019 17:20:37 +0000 Subject: [PATCH 1/7] Merged PR 11901: Get Min Prices for confgurable,grouped and bundle products. Get Min Prices for confgurable,grouped and bundle products. Related work items: #80912 --- .../Email/Model/Connector/Product.php | 165 ++++++++++++++++-- 1 file changed, 155 insertions(+), 10 deletions(-) diff --git a/code/Dotdigitalgroup/Email/Model/Connector/Product.php b/code/Dotdigitalgroup/Email/Model/Connector/Product.php index d4ff9ba98..9a762b0fa 100755 --- a/code/Dotdigitalgroup/Email/Model/Connector/Product.php +++ b/code/Dotdigitalgroup/Email/Model/Connector/Product.php @@ -87,19 +87,19 @@ public function __construct(Mage_Catalog_Model_Product $product) $options = Mage::getModel('catalog/product_visibility') ->getOptionArray(); $this->visibility = $options[$product->getVisibility()]; - $this->price = (float)number_format( - $product->getPrice(), 2, '.', '' - ); - $this->specialPrice = (float)number_format( - $product->getSpecialPrice(), 2, '.', '' - ); + + $this->getMinPrices($product); + $this->url = $product->getProductUrl(); $this->imagePath = Mage::getModel('catalog/product_media_config') ->getMediaUrl($product->getSmallImage()); $stock = Mage::getModel('cataloginventory/stock_item') ->loadByProduct($product); $this->stock = (float)number_format( - $stock->getQty(), 2, '.', '' + $stock->getQty(), + 2, + '.', + '' ); $shortDescription = $product->getShortDescription(); @@ -153,7 +153,10 @@ public function __construct(Mage_Catalog_Model_Product $product) $sOptions[$count]['sku'] = $selection->getSku(); $sOptions[$count]['id'] = $selection->getProductId(); $sOptions[$count]['price'] = (float)number_format( - $selection->getPrice(), 2, '.', '' + $selection->getPrice(), + 2, + '.', + '' ); $count++; } @@ -177,7 +180,10 @@ public function __construct(Mage_Catalog_Model_Product $product) foreach ($productAttribute['values'] as $attribute) { $options[$count]['option'] = $attribute['default_label']; $options[$count]['price'] = (float)number_format( - $attribute['pricing_value'], 2, '.', '' + $attribute['pricing_value'], + 2, + '.', + '' ); $count++; } @@ -196,4 +202,143 @@ public function expose() { return get_object_vars($this); } -} \ No newline at end of file + + /** + * Set the Minimum Prices for Configurable and Bundle products. + * + * @param Mage_Catalog_Model_Product $product + * + * @return null + */ + private function getMinPrices($product) + { + switch ($product->getTypeId()) { + case Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE: + $this->getMinConfigurablePrices($product); + break; + case Mage_Catalog_Model_Product_Type::TYPE_BUNDLE: + $this->getMinBundlePrices($product); + break; + case Mage_Catalog_Model_Product_Type::TYPE_GROUPED: + $this->getMinGroupedPrices($product); + break; + default: + $this->price = $product->getPrice(); + $this->specialPrice = $product->getSpecialPrice(); + } + + $this->formatPriceValues(); + } + + /** + * Calculates the Minimum Final Price and Special Price for the Configurable Products. + * + * @param Mage_Catalog_Model_Product $product + * + * @return null + */ + private function getMinConfigurablePrices($product) + { + foreach ($product->getTypeInstance()->getChildrenIds($product->getId()) as $childProductIds) { + foreach ($childProductIds as $id) { + $productById = Mage::getModel('catalog/product')->load($id); + $childPrices[] = $productById->getPrice(); + if ($productById->getSpecialPrice() !== null) { + $childSpecialPrices[] = $productById->getSpecialPrice(); + } + } + } + $this->price = isset($childPrices) ? min($childPrices) : null; + $this->specialPrice = isset($childSpecialPrices) ? min($childSpecialPrices) : null; + } + + /** + * Calculates the Minimum Final Price and Special Price for the Bundle Products. + * + * @param Mage_Catalog_Model_Product $product + * + * @return null + */ + private function getMinBundlePrices($product) + { + $this->price = 0; + $this->specialPrice = 0; + $productTypeInstance = $product->getTypeInstance(true); + $optionCol= $productTypeInstance->getOptionsCollection($product); + $selectionCol= $productTypeInstance->getSelectionsCollection( + $productTypeInstance->getOptionsIds($product), + $product + ); + $optionCol->appendSelections($selectionCol); + foreach ($optionCol as $option) { + if ($option->getRequired()) { + $selections = $option->getSelections(); + $specialPriceArrayFlag = []; + $minPrice = min(array_map(function ($s) use (&$specialPriceArrayFlag) { + if ($s->getSpecialPrice() > 0) { + $specialPriceArrayFlag[] = $s->getSpecialPrice(); + } + return $s->price; + }, $selections)); + $specialPriceSimpleProducts = min($specialPriceArrayFlag); + + if ($specialPriceSimpleProducts > 0) { + if ($product->getSpecialPrice() > 0) { + $specialPriceSimpleProducts = ($specialPriceSimpleProducts * $product->getSpecialPrice()/100); + $this->specialPrice += $specialPriceSimpleProducts; + } else { + $this->specialPrice += $specialPriceSimpleProducts; + } + } elseif ($product->getSpecialPrice() > 0) { + $minSpecialPrice = ($minPrice * $product->getSpecialPrice()/100); + $this->specialPrice += $minSpecialPrice; + } + + $this->price += round($minPrice, 2); + $this->specialPrice = ($this->price === $this->specialPrice) ? null : $this->specialPrice; + } + } + } + + /** + * Calculates the Minimum Final Price and Special Price for the Grouped Products. + * + * @param Mage_Catalog_Model_Product $product + * + * @return null + */ + private function getMinGroupedPrices($product) + { + $childProducts = $product->getTypeInstance(true)->getAssociatedProducts($product); + foreach ($childProducts as $childProduct) { + $childPrices[] = $childProduct->getPrice(); + if ($childProduct->getSpecialPrice() !== null) { + $childSpecialPrices[] = $childProduct->getSpecialPrice(); + } + } + $this->price = isset($childPrices) ? min($childPrices) : null; + $this->specialPrice = isset($childSpecialPrices) ? min($childSpecialPrices) : null; + } + + + /** + * Formats the price values. + * + * @return null + */ + private function formatPriceValues() + { + $this->price = (float)number_format( + $this->price, + 2, + '.', + '' + ); + $this->specialPrice = (float)number_format( + $this->specialPrice, + 2, + '.', + '' + ); + } +} From 75f7bbeb89d5a441e12dfb07777ab03b5552db6d Mon Sep 17 00:00:00 2001 From: Adeel Qamar Date: Wed, 27 Feb 2019 11:42:04 +0000 Subject: [PATCH 2/7] Merged PR 12133: Map re-subscribe import to address book On subscriber re-subscribe, if there is an address book id mapped, then re-subscribe contact to given address book. #83641 Related work items: #83641 --- .../Email/Model/Apiconnector/Client.php | 28 ++++++++++++++++++- .../Email/Model/Sync/Contact/Update.php | 6 ++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/code/Dotdigitalgroup/Email/Model/Apiconnector/Client.php b/code/Dotdigitalgroup/Email/Model/Apiconnector/Client.php index 2f60e7c43..f71717174 100755 --- a/code/Dotdigitalgroup/Email/Model/Apiconnector/Client.php +++ b/code/Dotdigitalgroup/Email/Model/Apiconnector/Client.php @@ -1534,5 +1534,31 @@ public function getCampaignByIdWithPreparedContent($campaignId) return $response; } - + + /** + * Resubscribes a previously unsubscribed contact to a given address book + * + * @param int $addressBookId + * @param string $email + * + * @return mixed + */ + public function postAddressBookContactResubscribe($addressBookId, $email) + { + $contact = array('unsubscribedContact' => array('email' => $email)); + $url = $this->getApiEndpoint() . self::REST_ADDRESS_BOOKS . $addressBookId + . '/contacts/resubscribe'; + $this->setUrl($url) + ->setVerb('POST') + ->buildPostBody($contact); + + $response = $this->execute(); + + if (isset($response->message)) { + $message = 'POST ADDRESS BOOK CONTACT RESUBSCRIBE' . $response->message; + Mage::helper('ddg')->log($message); + } + + return $response; + } } \ No newline at end of file diff --git a/code/Dotdigitalgroup/Email/Model/Sync/Contact/Update.php b/code/Dotdigitalgroup/Email/Model/Sync/Contact/Update.php index 26c3733f2..3699f6437 100644 --- a/code/Dotdigitalgroup/Email/Model/Sync/Contact/Update.php +++ b/code/Dotdigitalgroup/Email/Model/Sync/Contact/Update.php @@ -49,8 +49,10 @@ public function processCollection($collection) if (isset($apiContact->message) && $apiContact->message == Dotdigitalgroup_Email_Model_Apiconnector_Client::API_ERROR_CONTACT_SUPPRESSED ) { - $apiContact = $this->client->getContactByEmail($email); - $result = $this->client->postContactsResubscribe($apiContact); + $subscribersAddressBook = Mage::helper('ddg')->getSubscriberAddressBook($websiteId); + $result = ($subscribersAddressBook) ? + $this->client->postAddressBookContactResubscribe($subscribersAddressBook, $email) : + $this->client->postContactsResubscribe($this->client->getContactByEmail($email)); } } elseif ($item->getImportMode() == Dotdigitalgroup_Email_Model_Importer::MODE_SUBSCRIBER_UPDATE) { $email = $importData['email']; From 06d294ba5e1a12e4d5e2ae1af5a7604679505aaf Mon Sep 17 00:00:00 2001 From: Adeel Qamar Date: Thu, 28 Feb 2019 10:53:54 +0000 Subject: [PATCH 3/7] Merged PR 12217: Catalog sync performance improvement **Problem** During catalog sync we fetch all items from email_catalog table that need to be imported. There is no limit set on this this collection which results in adding all of collection's product_ids. This can result in 500 error or taking very long time to finish the sync. **Solution** Set limit on email_catalog collection Related work items: #84050 --- code/Dotdigitalgroup/Email/Model/Catalog.php | 1 + 1 file changed, 1 insertion(+) diff --git a/code/Dotdigitalgroup/Email/Model/Catalog.php b/code/Dotdigitalgroup/Email/Model/Catalog.php index b915cde02..1d6d688c0 100755 --- a/code/Dotdigitalgroup/Email/Model/Catalog.php +++ b/code/Dotdigitalgroup/Email/Model/Catalog.php @@ -274,6 +274,7 @@ protected function _getProductsToExport($store, $modified = false) 'imported', array('null' => 'true') ); } + $connectorCollection->getSelect()->limit($limit); if ($connectorCollection->getSize()) { $productIds = $connectorCollection->getColumnValues('product_id'); From 91651405a39409a964365aa77554345373a44747 Mon Sep 17 00:00:00 2001 From: Adeel Qamar Date: Mon, 11 Mar 2019 13:34:43 +0000 Subject: [PATCH 4/7] Merged PR 12423: Fix implode functions values #84691 Implode function expects first parameter to be glue string type and second parameter to be pieces of array type. Related work items: #84691 --- code/Dotdigitalgroup/Email/Model/Connector/Order.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/Dotdigitalgroup/Email/Model/Connector/Order.php b/code/Dotdigitalgroup/Email/Model/Connector/Order.php index 1874c9eb8..8003d009c 100755 --- a/code/Dotdigitalgroup/Email/Model/Connector/Order.php +++ b/code/Dotdigitalgroup/Email/Model/Connector/Order.php @@ -533,7 +533,7 @@ protected function _setOrderItems($orderData) $attributes[][$attributeCode] = $this->_limitLength($value); } elseif (is_array($value)) { - $value = implode($value, ', '); + $value = implode(', ', $value); $attributes[][$attributeCode] = $this->_limitLength($value); } From 9cc364c293a57056a821e5aff993fe558bfa9d20 Mon Sep 17 00:00:00 2001 From: Adeel Qamar Date: Mon, 11 Mar 2019 13:34:46 +0000 Subject: [PATCH 5/7] Merged PR 12367: Check if sweet tooth model is an object before calling method on it Sweet tooth model might not be an object. Check if returned value is an object. If not then do not set it. --- .../Email/Model/Apiconnector/Customer.php | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/code/Dotdigitalgroup/Email/Model/Apiconnector/Customer.php b/code/Dotdigitalgroup/Email/Model/Apiconnector/Customer.php index 5ad42da57..6e8224284 100755 --- a/code/Dotdigitalgroup/Email/Model/Apiconnector/Customer.php +++ b/code/Dotdigitalgroup/Email/Model/Apiconnector/Customer.php @@ -170,6 +170,11 @@ public function setRewardCustomer(Mage_Customer_Model_Customer $customer) //get tbt reward customer $tbtReward = Mage::getModel('rewards/customer') ->getRewardsCustomer($customer); + + if (! is_object($tbtReward)) { + return; + } + $this->rewardCustomer = $tbtReward; //get transfers collection from tbt reward. only active and order by last updated. @@ -722,6 +727,9 @@ public function getRewardReferralUrl() */ public function getRewardPointBalance() { + if (! isset($this->rewardCustomer)) { + return 0; + } return $this->cleanString($this->rewardCustomer->getPointsSummary()); } @@ -730,9 +738,10 @@ public function getRewardPointBalance() */ public function getRewardPointPending() { - return $this->cleanString( - $this->rewardCustomer->getPendingPointsSummary() - ); + if (! isset($this->rewardCustomer)) { + return 0; + } + return $this->cleanString($this->rewardCustomer->getPendingPointsSummary()); } /** @@ -740,9 +749,10 @@ public function getRewardPointPending() */ public function getRewardPointPendingTime() { - return $this->cleanString( - $this->rewardCustomer->getPendingTimePointsSummary() - ); + if (! isset($this->rewardCustomer)) { + return 0; + } + return $this->cleanString($this->rewardCustomer->getPendingTimePointsSummary()); } /** @@ -750,9 +760,10 @@ public function getRewardPointPendingTime() */ public function getRewardPointOnHold() { - return $this->cleanString( - $this->rewardCustomer->getOnHoldPointsSummary() - ); + if (! isset($this->rewardCustomer)) { + return 0; + } + return $this->cleanString($this->rewardCustomer->getOnHoldPointsSummary()); } /** From 28a42bc5550ce0bae44295ad1ac6be2eb3682370 Mon Sep 17 00:00:00 2001 From: Adeel Qamar Date: Tue, 12 Mar 2019 10:26:37 +0000 Subject: [PATCH 6/7] Merged PR 12471: update version update version --- code/Dotdigitalgroup/Email/etc/config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/Dotdigitalgroup/Email/etc/config.xml b/code/Dotdigitalgroup/Email/etc/config.xml index aec82c45f..3f8fd6b77 100755 --- a/code/Dotdigitalgroup/Email/etc/config.xml +++ b/code/Dotdigitalgroup/Email/etc/config.xml @@ -2,7 +2,7 @@ - 6.4.11 + 6.4.12 From 063cb31735c5bf66a5c16011a6c8a8ee17536d28 Mon Sep 17 00:00:00 2001 From: Adeel Qamar Date: Wed, 13 Mar 2019 11:02:43 +0000 Subject: [PATCH 7/7] Merged PR 12511: update readme 6.4.12 --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 1c33a2f99..e9c5e6a8a 100755 --- a/README.md +++ b/README.md @@ -34,6 +34,18 @@ You are welcome to contribute to Engagement Cloud for Magento! You can either: - Fix a bug: please fork this repo and submit the Pull Request to our [Develop branch](https://github.com/dotmailer/dotmailer-magento-extension/tree/develop) Request a feature on our [roadmap](https://roadmap.dotdigital.com) +# V6.4.12 + +##### Improvements + + * We've improved the performance of the product sync process by setting a limit when querying products before import + * Resubscribed contacts weren't added back to the "subscriber" address book; they are now + * Configurable, grouped, and bundled products are now correctly synced to Engagement Cloud with the lowest relevant price of their children + +##### Bug Fixes + * We've fixed incorrect syntax in the Order model (#309) + * We've fixed an issue that caused errors within the Sweet Tooth (Smile.io) integration + # V6.4.11 ##### Improvements