-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #13 from code4business/develop
- Loading branch information
Showing
12 changed files
with
247 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
|
||
namespace C4B\FreeProduct\Observer; | ||
|
||
use C4B\FreeProduct\SalesRule\Action\ForeachGiftAction; | ||
use C4B\FreeProduct\SalesRule\Action\GiftAction; | ||
|
||
use Magento\Framework\Event\Observer; | ||
|
@@ -10,9 +11,19 @@ | |
use Magento\Quote\Model\Quote; | ||
|
||
/** | ||
* Observer for resetting gift cart items | ||
* Observer for resetting gift cart items. | ||
* When quote totals are collected, all gifts are removed and are later re-added by Discount total collector. | ||
* It is triggered by two events: | ||
* - quote collect before: for normal quote operations (adding items, changing qty, removing item) | ||
* - address collect before: When shipping is estimated the above event is not triggered. | ||
* | ||
* There is some weird handling of quote items. There are two ways to get them: getItems() and getItemsCollection() | ||
* New quote items are added into the collection, but not into getItems. This is apparently how it should be because | ||
* otherwise newly added quote items are added again since they don't have an item_id yet and in case of bundle items this would fail. | ||
* So quote->setItems should not be used here: | ||
* @see \Magento\Quote\Model\QuoteRepository\SaveHandler::save | ||
* @see \Magento\Quote\Model\Quote\Item\CartItemPersister::save | ||
* | ||
* @category C4B | ||
* @package C4B_FreeProduct | ||
* @author Dominik Meglič <[email protected]> | ||
* @copyright code4business Software GmbH | ||
|
@@ -21,45 +32,77 @@ | |
class ResetGiftItems implements ObserverInterface | ||
{ | ||
/** | ||
* Delete all gift items. They will be re-added by SalesRule (If possible). | ||
* | ||
* @var bool | ||
*/ | ||
private $areGiftItemsReset = false; | ||
|
||
/** | ||
* @event sales_quote_collect_totals_before | ||
* @event sales_quote_address_collect_totals_before | ||
* @param Observer $observer | ||
* @return void | ||
* @throws \Exception | ||
*/ | ||
public function execute(\Magento\Framework\Event\Observer $observer) | ||
public function execute(Observer $observer) | ||
{ | ||
/** @var ShippingAssignmentInterface $shippingAssignment */ | ||
$shippingAssignment = $observer->getEvent()->getData('shipping_assignment'); | ||
/** @var Quote $quote */ | ||
$quote = $observer->getEvent()->getData('quote'); | ||
/** @var Quote\Address $address */ | ||
$address = $shippingAssignment->getShipping()->getAddress(); | ||
/** @var ShippingAssignmentInterface $shippingAssignment */ | ||
$shippingAssignment = $observer->getEvent()->getData('shipping_assignment'); | ||
|
||
if ($shippingAssignment->getItems() == null || $address->getAddressType() != Quote\Address::TYPE_SHIPPING) | ||
if ($quote->getItems() == null || $this->areGiftItemsReset) | ||
{ | ||
return; | ||
} | ||
|
||
$newShippingAssignmentItems = $this->removeOldGiftQuoteItems($shippingAssignment); | ||
if ($shippingAssignment instanceof ShippingAssignmentInterface) | ||
{ | ||
/** @var Quote\Address $address */ | ||
$address = $shippingAssignment->getShipping()->getAddress(); | ||
|
||
$shippingAssignment->setItems($newShippingAssignmentItems); | ||
if ($address->getAddressType() != Quote\Address::ADDRESS_TYPE_SHIPPING) | ||
{ | ||
return; | ||
} | ||
} | ||
else | ||
{ | ||
$address = $quote->getShippingAddress(); | ||
} | ||
|
||
$realQuoteItems = $this->removeOldGiftQuoteItems($quote->getItemsCollection()); | ||
$this->areGiftItemsReset = true; | ||
$address->unsetData(GiftAction::APPLIED_FREEPRODUCT_RULE_IDS); | ||
$address->unsetData('cached_items_all'); | ||
|
||
$this->updateExtensionAttributes($quote, $shippingAssignment); | ||
if ($shippingAssignment instanceof ShippingAssignmentInterface) | ||
{ | ||
$shippingAssignment->setItems($realQuoteItems); | ||
$this->updateExtensionAttributes($quote, $shippingAssignment); | ||
} | ||
} | ||
|
||
/** | ||
* @param ShippingAssignmentInterface $shippingAssignment | ||
* @return array | ||
* A new gift item was added so if cart totals are collected again, all gift items will be reset. | ||
* | ||
* @return void | ||
*/ | ||
public function reportGiftItemAdded() | ||
{ | ||
$this->areGiftItemsReset = false; | ||
} | ||
|
||
/** | ||
* @param \Magento\Quote\Model\ResourceModel\Quote\Item\Collection|\Magento\Framework\Data\Collection $quoteItemsCollection | ||
* @return Quote\Item[] | ||
* @throws \Exception | ||
*/ | ||
protected function removeOldGiftQuoteItems($shippingAssignment): array | ||
protected function removeOldGiftQuoteItems($quoteItemsCollection) | ||
{ | ||
$newShippingAssignment = []; | ||
$realQuoteItems = []; | ||
|
||
/** @var Quote\Item $quoteItem */ | ||
foreach ($shippingAssignment->getItems() as $quoteItem) | ||
foreach ($quoteItemsCollection->getItems() as $key => $quoteItem) | ||
{ | ||
if ($quoteItem->isDeleted()) | ||
{ | ||
|
@@ -78,15 +121,12 @@ protected function removeOldGiftQuoteItems($shippingAssignment): array | |
} | ||
} else | ||
{ | ||
/** | ||
* Reset shipping assignment to prevent others from working on old items | ||
* @see \Magento\Tax\Model\Sales\Total\Quote\CommonTaxCollector::processAppliedTaxes | ||
* @see \Magento\Tax\Model\Plugin\OrderSave::saveOrderTax | ||
*/ | ||
$newShippingAssignment[] = $quoteItem; | ||
$quoteItem->unsetData(ForeachGiftAction::APPLIED_FREEPRODUCT_RULE_IDS); | ||
$realQuoteItems[$key] = $quoteItem; | ||
} | ||
} | ||
return $newShippingAssignment; | ||
|
||
return $realQuoteItems; | ||
} | ||
|
||
/** | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
<?php | ||
|
||
namespace C4B\FreeProduct\SalesRule\Action; | ||
|
||
use C4B\FreeProduct\Observer\ResetGiftItems; | ||
use Magento\Catalog\Api\ProductRepositoryInterface; | ||
use Magento\Customer\Model\Address; | ||
use Magento\Quote\Model\Quote\Item\AbstractItem; | ||
use Magento\SalesRule\Model\Rule\Action\Discount; | ||
use Psr\Log\LoggerInterface; | ||
|
||
/** | ||
* Adds a gift for each cart item that meets criteria. It is also multiplied by the qty of said cart item. | ||
* Ex. Add one gift for each product from category Sales. | ||
* | ||
* @package C4B_FreeProduct | ||
* @author Dominik Meglič <[email protected]> | ||
* @copyright code4business Software GmbH | ||
* @license http://opensource.org/licenses/osl-3.0.php | ||
*/ | ||
class ForeachGiftAction extends GiftAction | ||
{ | ||
const ACTION = 'add_gift_foreach'; | ||
/** | ||
* @var ResetGiftItems | ||
*/ | ||
private $resetGiftItems; | ||
/** | ||
* @var LoggerInterface | ||
*/ | ||
private $logger; | ||
|
||
|
||
/** | ||
* @param Discount\DataFactory $discountDataFactory | ||
* @param ProductRepositoryInterface $productRepository | ||
* @param ResetGiftItems $resetGiftItems | ||
* @param LoggerInterface $logger | ||
*/ | ||
public function __construct(Discount\DataFactory $discountDataFactory, | ||
ProductRepositoryInterface $productRepository, | ||
ResetGiftItems $resetGiftItems, | ||
LoggerInterface $logger) | ||
{ | ||
parent::__construct($discountDataFactory, $productRepository, $resetGiftItems, $logger); | ||
$this->resetGiftItems = $resetGiftItems; | ||
$this->logger = $logger; | ||
} | ||
|
||
/** | ||
* @param \Magento\SalesRule\Model\Rule $rule | ||
* @param AbstractItem $item | ||
* @param float $qty | ||
* @return Discount\Data | ||
*/ | ||
public function calculate($rule, $item, $qty) | ||
{ | ||
$appliedRuleIds = $item->getData(static::APPLIED_FREEPRODUCT_RULE_IDS); | ||
|
||
if ($item->getAddress()->getAddressType() != Address::TYPE_SHIPPING | ||
|| ($appliedRuleIds != null && isset($appliedRuleIds[$rule->getId()]))) | ||
{ | ||
return $this->getDiscountData($item); | ||
} | ||
|
||
$skus = explode(',', $rule->getData(static::RULE_DATA_KEY_SKU)); | ||
$isRuleAdded = false; | ||
|
||
foreach ($skus as $sku) | ||
{ | ||
try | ||
{ | ||
$quoteItem = $item->getQuote()->addProduct($this->getGiftProduct($sku), $rule->getDiscountAmount() * $qty); | ||
$item->getQuote()->setItemsCount($item->getQuote()->getItemsCount() + 1); | ||
$item->getQuote()->setItemsQty((float)$item->getQuote()->getItemsQty() + $quoteItem->getQty()); | ||
$this->resetGiftItems->reportGiftItemAdded(); | ||
|
||
if (is_string($quoteItem)) | ||
{ | ||
throw new \Exception($quoteItem); | ||
} | ||
|
||
$isRuleAdded = true; | ||
} catch (\Exception $e) | ||
{ | ||
$this->logger->error( | ||
sprintf('Exception occurred while adding gift product %s to cart. Rule: %d, Exception: %s', implode(',', $skus), $rule->getId(), $e->getMessage()), | ||
[__METHOD__] | ||
); | ||
} | ||
} | ||
if ($isRuleAdded) | ||
{ | ||
$this->addAppliedRuleIdToItem($rule->getRuleId(), $item); | ||
} | ||
|
||
return $this->getDiscountData($item); | ||
} | ||
|
||
/** | ||
* @param int $ruleId | ||
* @param AbstractItem $quoteItem | ||
*/ | ||
protected function addAppliedRuleIdToItem(int $ruleId, AbstractItem $quoteItem) | ||
{ | ||
$appliedRules = $quoteItem->getData(static::APPLIED_FREEPRODUCT_RULE_IDS); | ||
|
||
if ($appliedRules == null) | ||
{ | ||
$appliedRules = []; | ||
} | ||
|
||
$appliedRules[$ruleId] = $ruleId; | ||
|
||
$quoteItem->setData(static::APPLIED_FREEPRODUCT_RULE_IDS, $appliedRules); | ||
} | ||
} |
Oops, something went wrong.