Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7547ddf
move currency to order, change escapers and remove developer id.
Bashev Apr 8, 2023
877d5da
changes to pass semantic version and Code style checks.
Bashev Apr 8, 2023
6802680
changes to pass semantic version tests checks.
Bashev Apr 8, 2023
167d5b1
cast store id.
Bashev Apr 8, 2023
fc3fe20
add coupon and fix unit tests.
Bashev Apr 11, 2023
dc95870
Merge branch '2.4-develop' into Issue-37348-gtag-ecommerce-tracking
Bashev Apr 11, 2023
53479e9
add proper unit tests
Bashev Apr 13, 2023
eaf5168
add dependencies.
Bashev Apr 13, 2023
e595cce
Delete phpunit.xml.dist
Bashev Apr 18, 2023
4afa7e1
Merge branch 'magento:2.4-develop' into Issue-37348-gtag-ecommerce-tr…
Bashev Apr 18, 2023
55b63dc
revert phpunit.xml.dist
Bashev Apr 18, 2023
7c6e9bc
cast GrandTotal value to float.
Bashev May 1, 2023
7f4f6d9
Merge branch '2.4-develop' into Issue-37348-gtag-ecommerce-tracking
Bashev May 1, 2023
813e82f
Merge branch '2.4-develop' into gtag-ecommerce-tracking-improvements
Bashev May 16, 2023
170987a
Improvements on GoogleGTag resolve issue #37348
Bashev May 16, 2023
cef14af
Merge branch '2.4-develop' into gtag-ecommerce-tracking-improvements
Bashev Sep 13, 2023
258b38b
Merge branch '2.4-develop' into gtag-ecommerce-tracking-improvements
engcom-Hotel Sep 18, 2023
0239c25
add backward compatibility.
Bashev Sep 18, 2023
6ccbbb1
Merge remote-tracking branch 'origin/gtag-ecommerce-tracking-improvem…
Bashev Sep 18, 2023
2124629
add backward compatibility.
Bashev Sep 18, 2023
a61f475
Fixed Static test failure
engcom-Echo Sep 27, 2023
88fff24
Fixed static test failure
engcom-Echo Sep 27, 2023
98f674f
Fixed static test failure
engcom-Echo Sep 27, 2023
9edf2ff
Fixed Unit Test failure
engcom-Echo Oct 4, 2023
dc01c68
Merge branch '2.4-develop' into gtag-ecommerce-tracking-improvements
engcom-Echo Dec 13, 2023
0f3444e
Merge branch '2.4-develop' into gtag-ecommerce-tracking-improvements
engcom-Echo Dec 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 55 additions & 18 deletions app/code/Magento/GoogleGtag/Block/Ga.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@

namespace Magento\GoogleGtag\Block;

use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Cookie\Helper\Cookie;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\Serialize\SerializerInterface;
use Magento\Framework\View\Element\Template;
use Magento\Framework\View\Element\Template\Context;
Expand All @@ -25,27 +27,32 @@ class Ga extends Template
/**
* @var GtagConfiguration
*/
private $googleGtagConfig;
private GtagConfiguration $googleGtagConfig;

/**
* @var Cookie
*/
private $cookieHelper;
private Cookie $cookieHelper;

/**
* @var SerializerInterface
*/
private $serializer;
private SerializerInterface $serializer;

/**
* @var OrderRepositoryInterface
*/
private $orderRepository;
private OrderRepositoryInterface $orderRepository;

/**
* @var SearchCriteriaBuilder
*/
private $searchCriteriaBuilder;
private SearchCriteriaBuilder $searchCriteriaBuilder;

/**
* @var ProductRepositoryInterface
*/
private ProductRepositoryInterface $productRepository;

/**
* @param Context $context
Expand All @@ -55,6 +62,7 @@ class Ga extends Template
* @param SearchCriteriaBuilder $searchCriteriaBuilder
* @param OrderRepositoryInterface $orderRepository
* @param array $data
* @param ProductRepositoryInterface|null $productRepository
*/
public function __construct(
Context $context,
Expand All @@ -63,13 +71,17 @@ public function __construct(
SerializerInterface $serializer,
SearchCriteriaBuilder $searchCriteriaBuilder,
OrderRepositoryInterface $orderRepository,
array $data = []
array $data = [],
ProductRepositoryInterface $productRepository = null
) {
$this->googleGtagConfig = $googleGtagConfig;
$this->cookieHelper = $cookieHelper;
$this->serializer = $serializer;
$this->orderRepository = $orderRepository;
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
$this->productRepository = $productRepository ?:
ObjectManager::getInstance()->get(ProductRepositoryInterface::class);

parent::__construct($context, $data);
}

Expand Down Expand Up @@ -97,6 +109,7 @@ public function isCookieRestrictionModeEnabled(): bool
* Return current website id.
*
* @return int
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function getCurrentWebsiteId(): int
{
Expand All @@ -116,7 +129,7 @@ public function getPageTrackingData($measurementId): array
{
return [
'optPageUrl' => $this->getOptPageUrl(),
'measurementId' => $this->escapeHtmlAttr($measurementId, false)
'measurementId' => $measurementId
];
}

Expand All @@ -129,11 +142,12 @@ public function getPageTrackingData($measurementId): array
* @link https://developers.google.com/gtagjs/reference/event#purchase
*
* @return array
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function getOrdersTrackingData(): array
{
$result = [];
$orderIds = $this->getOrderIds();
$orderIds = $this->getData('order_ids');
if (empty($orderIds) || !is_array($orderIds)) {
return $result;
}
Expand All @@ -145,24 +159,45 @@ public function getOrdersTrackingData(): array
$collection = $this->orderRepository->getList($this->searchCriteriaBuilder->create());

foreach ($collection->getItems() as $order) {
foreach ($order->getAllVisibleItems() as $item) {
$result['products'][] = [
'item_id' => $this->escapeJsQuote($item->getSku()),
'item_name' => $this->escapeJsQuote($item->getName()),
'price' => number_format((float) $item->getPrice(), 2),
'quantity' => (int)$item->getQtyOrdered(),
foreach ($order->getAllVisibleItems() as $index => $item) {
$product = $this->productRepository->get($item->getSku());
$orderProduct = [
'index' => $index+1,
'item_id' => $item->getSku(),
'item_name' => $item->getName(),
'item_brand' => $product->getAttributeText('manufacturer'),
'affiliation' => $this->_storeManager->getStore()->getFrontendName(),
'price' => round((float) $item->getPrice(), 2),
'quantity' => (int)$item->getQtyOrdered()
];

if ($item->getDiscountAmount() > 0) {
$orderProduct['discount'] = $item->getDiscountAmount();

if (!empty($order->getCouponCode())) {
$orderProduct['coupon'] = $order->getCouponCode();
}
}

$result['products'][] = $orderProduct;
}
$result['orders'][] = [

$resultOrder = [
'transaction_id' => $order->getIncrementId(),
'affiliation' => $this->escapeJsQuote($this->_storeManager->getStore()->getFrontendName()),
'value' => number_format((float) $order->getGrandTotal(), 2),
'tax' => number_format((float) $order->getTaxAmount(), 2),
'shipping' => number_format((float) $order->getShippingAmount(), 2),
'currency' => $order->getOrderCurrencyCode(),
];
$result['currency'] = $order->getOrderCurrencyCode();

if (!empty($order->getCouponCode())) {
$resultOrder['coupon'] = $order->getCouponCode();
}

$result['orders'][] = $resultOrder;
}

return $result;
}

Expand All @@ -175,8 +210,8 @@ private function getOptPageUrl(): string
{
$optPageURL = '';
$pageName = $this->getPageName() !== null ? trim($this->getPageName()) : '';
if ($pageName && substr($pageName, 0, 1) === '/' && strlen($pageName) > 1) {
$optPageURL = ", '" . $this->escapeHtmlAttr($pageName, false) . "'";
if ($pageName && str_starts_with($pageName, '/') && strlen($pageName) > 1) {
$optPageURL = ", '" . $pageName . "'";
}
return $optPageURL;
}
Expand All @@ -185,6 +220,7 @@ private function getOptPageUrl(): string
* Provide analytics events data
*
* @return bool|string
* @throws \Magento\Framework\Exception\NoSuchEntityException|\Magento\Framework\Exception\LocalizedException
*/
public function getAnalyticsData()
{
Expand All @@ -196,6 +232,7 @@ public function getAnalyticsData()
'ordersTrackingData' => $this->getOrdersTrackingData(),
'googleAnalyticsAvailable' => $this->googleGtagConfig->isGoogleAnalyticsAvailable()
];

return $this->serializer->serialize($analyticData);
}
}
72 changes: 58 additions & 14 deletions app/code/Magento/GoogleGtag/Test/Unit/Block/GaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

namespace Magento\GoogleGtag\Test\Unit\Block;

use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\Product;
use Magento\Cookie\Helper\Cookie;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\Api\SearchCriteriaInterface;
Expand Down Expand Up @@ -52,6 +54,11 @@ class GaTest extends TestCase
*/
private $storeMock;

/**
* @var MockObject
*/
private $productMock;

/**
* @var GtagConfiguration|mixed|MockObject
*/
Expand All @@ -61,29 +68,41 @@ class GaTest extends TestCase
* @var SearchCriteriaBuilder|mixed|MockObject
*/
private $searchCriteriaBuilder;

/**
* @var OrderRepositoryInterface|mixed|MockObject
*/
private $orderRepository;

/**
* @var OrderRepositoryInterface|mixed|MockObject
*/
private $productRepository;

/**
* @var SerializerInterface|mixed|MockObject
*/
private $serializerMock;

/**
* @var Escaper|MockObject
*/
private $escaperMock;

protected function setUp(): void
{
$objectManager = new ObjectManager($this);
$contextMock = $this->getMockBuilder(Context::class)
->disableOriginalConstructor()
->getMock();

$contextMock->expects($this->once())
->method('getEscaper')
->willReturn($objectManager->getObject(Escaper::class));

$this->escaperMock = $this->getMockBuilder(Escaper::class)
->disableOriginalConstructor()
->getMock();
$this->escaperMock->expects($this->any())->method('escapeJsQuote')->with('test')->willReturn('test');
$this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class)
->disableOriginalConstructor()
->getMockForAbstractClass();

$this->serializerMock = $this->getMockBuilder(SerializerInterface::class)
->disableOriginalConstructor()
->getMockForAbstractClass();
Expand All @@ -92,6 +111,7 @@ protected function setUp(): void
->disableOriginalConstructor()
->getMock();
$contextMock->expects($this->once())->method('getStoreManager')->willReturn($this->storeManagerMock);
$contextMock->expects($this->once())->method('getEscaper')->willReturn($this->escaperMock);

$this->orderRepository = $this->getMockBuilder(OrderRepositoryInterface::class)
->onlyMethods(['getList'])
Expand All @@ -102,6 +122,18 @@ protected function setUp(): void
->disableOriginalConstructor()
->getMockForAbstractClass();

$this->productRepository = $this->getMockBuilder(ProductRepositoryInterface::class)
->onlyMethods(['get'])
->disableOriginalConstructor()
->getMockForAbstractClass();

$this->productMock = $this->getMockBuilder(Product::class)
->disableOriginalConstructor()
->onlyMethods(['getAttributeText'])
->getMock();

$this->productRepository->expects($this->any())->method('get')->willReturn($this->productMock);

$this->googleGtagConfig = $this->getMockBuilder(GtagConfiguration::class)
->disableOriginalConstructor()
->getMock();
Expand All @@ -118,7 +150,8 @@ protected function setUp(): void
'cookieHelper' => $this->cookieHelperMock,
'serializer' => $this->serializerMock,
'searchCriteriaBuilder' => $this->searchCriteriaBuilder,
'orderRepository' => $this->orderRepository
'orderRepository' => $this->orderRepository,
'productRepository' => $this->productRepository
]
);
}
Expand All @@ -142,6 +175,7 @@ public function testGetCurrentWebsiteId()
/**
* Test for getOrdersTrackingData()
* @return void
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function testOrderTrackingData()
{
Expand All @@ -157,28 +191,36 @@ public function testOrderTrackingData()
$orderSearchResult->method('getTotalCount')->willReturn(1);
$orderSearchResult->method('getItems')->willReturn([ 1 => $this->createOrderMock(1)]);
$this->searchCriteriaBuilder->method('create')->willReturn($searchCriteria);
$this->storeMock->expects($this->once())->method('getFrontendName')->willReturn('test');
$this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock);
$this->storeMock->expects($this->atLeastOnce())->method('getFrontendName')->willReturn('test');
$this->storeManagerMock->expects($this->atLeastOnce())->method('getStore')->willReturn($this->storeMock);
$this->productMock->expects($this->once())->method('getAttributeText')
->with('manufacturer')->willReturn('Brand 1');

$expectedResult = [
'orders' => [
[
'transaction_id' => 100,
'affiliation' => 'test',
'currency' => 'USD',
'value' => 10.00,
'tax' => 2.00,
'shipping' => 1.00,
'currency' => 'USD'
'coupon' => 'coupon1',
'affiliation' => 'test',
]
],
'products' => [
[
'index' => 1,
'item_id' => 'sku0',
'item_name' => 'testName0',
'item_brand' => 'Brand 1',
'affiliation' => 'test',
'price' => 0.00,
'quantity' => 1
'quantity' => 1,
'discount' => 0.01,
'coupon' => 'coupon1',
]
],
'currency' => 'USD'
];
$this->gaBlock->setOrderIds([1, 2]);
$tempResults = $this->gaBlock->getOrdersTrackingData();
Expand Down Expand Up @@ -209,10 +251,11 @@ protected function createOrderMock($orderItemCount = 1)
$orderItemMock = $this->getMockBuilder(OrderItemInterface::class)
->disableOriginalConstructor()
->getMockForAbstractClass();
$orderItemMock->expects($this->once())->method('getSku')->willReturn('sku' . $i);
$orderItemMock->expects($this->exactly(2))->method('getSku')->willReturn('sku' . $i);
$orderItemMock->expects($this->once())->method('getName')->willReturn('testName' . $i);
$orderItemMock->expects($this->once())->method('getPrice')->willReturn($i . '.00');
$orderItemMock->expects($this->once())->method('getQtyOrdered')->willReturn($i + 1);
$orderItemMock->expects($this->exactly(2))->method('getDiscountAmount')->willReturn(0.01);
$orderItems[] = $orderItemMock;
}

Expand All @@ -224,7 +267,8 @@ protected function createOrderMock($orderItemCount = 1)
$orderMock->expects($this->once())->method('getGrandTotal')->willReturn(10);
$orderMock->expects($this->once())->method('getTaxAmount')->willReturn(2);
$orderMock->expects($this->once())->method('getShippingAmount')->willReturn($orderItemCount);
$orderMock->expects($this->exactly(2))->method('getOrderCurrencyCode')->willReturn('USD');
$orderMock->expects($this->once())->method('getOrderCurrencyCode')->willReturn('USD');
$orderMock->expects($this->exactly(4))->method('getCouponCode')->willReturn('coupon1');
return $orderMock;
}

Expand Down
1 change: 1 addition & 0 deletions app/code/Magento/GoogleGtag/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"require": {
"php": "~8.1.0||~8.2.0",
"magento/framework": "*",
"magento/module-catalog": "*",
"magento/module-cookie": "*",
"magento/module-sales": "*",
"magento/module-store": "*"
Expand Down
1 change: 1 addition & 0 deletions app/code/Magento/GoogleGtag/etc/module.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Magento_GoogleGtag" >
<sequence>
<module name="Magento_Catalog"/>
<module name="Magento_Store"/>
<module name="Magento_Checkout"/>
</sequence>
Expand Down
3 changes: 2 additions & 1 deletion app/code/Magento/GoogleGtag/view/frontend/templates/ga.phtml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ $analyticsData = $block->getAnalyticsData();
<script type="text/x-magento-init">
{
"*": {
"Magento_GoogleGtag/js/google-analytics": <?= /* @noEscape */ $block->getAnalyticsData() ?>
"Magento_GoogleGtag/js/google-analytics":
<?= /* @noEscape */ $escaper->escapeHtml($escaper->escapeJs($block->getAnalyticsData())) ?>
}
}
</script>
Expand Down
Loading