Skip to content

Commit be5e377

Browse files
Merge pull request #149 from magebitcom/feature/LMM-100
Feature/lmm 100
2 parents b60f998 + 7052e26 commit be5e377

File tree

4 files changed

+220
-3
lines changed

4 files changed

+220
-3
lines changed
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Leanpay\Payment\Plugin\Block\Product\View;
6+
7+
use Leanpay\Payment\Block\Installment\Pricing\Render\TemplatePriceBox;
8+
use Magento\Catalog\Block\Product\View as ProductView;
9+
use Magento\Catalog\Block\Product\View\Options as OptionsBlock;
10+
use Magento\Framework\Serialize\SerializerInterface;
11+
12+
class ViewPlugin
13+
{
14+
/**
15+
* @var SerializerInterface
16+
*/
17+
private $serializer;
18+
19+
/**
20+
* @var TemplatePriceBox
21+
*/
22+
private $template;
23+
24+
/**
25+
* @var array
26+
*/
27+
private $templateCache = [];
28+
29+
/**
30+
* @param \Magento\Framework\Serialize\SerializerInterface $serializer
31+
* @param \Leanpay\Payment\Block\Installment\Pricing\Render\TemplatePriceBox $templatePriceBox
32+
*/
33+
public function __construct(
34+
SerializerInterface $serializer,
35+
TemplatePriceBox $templatePriceBox
36+
) {
37+
$this->serializer = $serializer;
38+
$this->template = $templatePriceBox;
39+
}
40+
41+
/**
42+
* Add installment html map to product view price JSON.
43+
*
44+
* @param \Magento\Catalog\Block\Product\View $subject
45+
* @param string $result
46+
* @return string
47+
*/
48+
public function afterGetJsonConfig(ProductView $subject, $result)
49+
{
50+
try {
51+
// Only PDP: enforce full action name to avoid category/listing pages
52+
$request = $subject->getRequest();
53+
if ($request->getFullActionName() !== 'catalog_product_view') {
54+
return $result;
55+
}
56+
57+
$json = $this->serializer->unserialize($result);
58+
59+
if (!isset($json['prices'])) {
60+
return $result;
61+
}
62+
63+
$amounts = [];
64+
foreach ($json['prices'] as $price) {
65+
if (isset($price['amount'])) {
66+
$amounts[] = $price['amount'];
67+
}
68+
}
69+
70+
// Collect likely totals from customizable options (single selections)
71+
try {
72+
/** @var OptionsBlock $optionsBlock */
73+
$optionsBlock = $subject->getLayout()->createBlock(OptionsBlock::class);
74+
if ($optionsBlock) {
75+
$optionsBlock->setProduct($subject->getProduct());
76+
$optionsJson = $optionsBlock->getJsonConfig();
77+
$optionsConfig = $this->serializer->unserialize($optionsJson);
78+
79+
$baseFinal = isset($json['prices']['finalPrice']['amount'])
80+
? (float)$json['prices']['finalPrice']['amount']
81+
: 0.0;
82+
83+
foreach ($optionsConfig as $opt) {
84+
if (is_array($opt)) {
85+
foreach ($opt as $valueConfig) {
86+
if (isset($valueConfig['prices']['finalPrice']['amount'])) {
87+
$total = $baseFinal + (float)$valueConfig['prices']['finalPrice']['amount'];
88+
$amounts[] = $total;
89+
}
90+
}
91+
} else {
92+
if (isset($opt['prices']['finalPrice']['amount'])) {
93+
$total = $baseFinal + (float)$opt['prices']['finalPrice']['amount'];
94+
$amounts[] = $total;
95+
}
96+
}
97+
}
98+
}
99+
} catch (\Throwable $e) {
100+
// best-effort enrichment
101+
}
102+
103+
$amounts = array_unique(array_map(function ($v) {
104+
return (int)round((float)$v);
105+
}, $amounts));
106+
107+
$installmentMap = [];
108+
foreach ($amounts as $intAmount) {
109+
$installmentMap[$intAmount] = $this->getHtmlFromCache($intAmount);
110+
}
111+
112+
$json['installmentHtmlMap'] = $installmentMap;
113+
114+
return $this->serializer->serialize($json);
115+
} catch (\Throwable $e) {
116+
return $result;
117+
}
118+
}
119+
120+
/**
121+
* Get installment html amount from cache
122+
*
123+
* @param float $amount
124+
* @return mixed|string
125+
*/
126+
private function getHtmlFromCache($amount): string
127+
{
128+
$int = intval(round($amount));
129+
if (!isset($this->templateCache[$int])) {
130+
$this->templateCache[$int] = $this->template->setData('amount', $amount)->toHtml();
131+
}
132+
133+
return $this->templateCache[$int];
134+
}
135+
}

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"magento/framework": ">=103.0.1"
77
},
88
"type": "magento2-module",
9-
"version": "0.10.1",
9+
"version": "0.11.0",
1010
"autoload": {
1111
"files": [
1212
"registration.php"

etc/di.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@
8383
</type>
8484

8585
<!-- Plugins -->
86+
<type name="Magento\Catalog\Block\Product\View">
87+
<plugin name="LeanpayInstallmentViewPlugin" type="Leanpay\Payment\Plugin\Block\Product\View\ViewPlugin" sortOrder="10"/>
88+
</type>
8689
<type name="Magento\Payment\Model\Checks\TotalMinMax">
8790
<plugin name="TotalMinMaxLeanpay" type="Leanpay\Payment\Plugin\TotalMinMaxPlugin" sortOrder="1"/>
8891
</type>

view/frontend/web/js/price-box-mixin.js

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ define([
1111
'Magento_Catalog/js/price-utils',
1212
'underscore',
1313
'mage/template',
14-
'jquery-ui-modules/widget'
15-
], function ($, utils, _, mageTemplate) {
14+
'jquery-ui-modules/widget',
15+
'mage/url'
16+
], function ($, utils, _, mageTemplate, jqueryWidget, url) {
1617
'use strict';
1718

1819
var globalOptions = {
@@ -24,11 +25,85 @@ define([
2425

2526
return function (widget) {
2627
$.widget('mage.priceBox', widget, {
28+
_ensureInstallmentCache: function ()
29+
{
30+
if (typeof this.installmentCache === 'undefined') {
31+
this.installmentCache = {};
32+
}
33+
},
34+
installmentPrice: function (amount)
35+
{
36+
var self = this;
37+
self._ensureInstallmentCache();
38+
39+
// Guard: only on PDP pages
40+
if (!$('body').hasClass('catalog-product-view')) {
41+
return;
42+
}
43+
44+
var intAmount = Math.round(amount);
45+
46+
// Prefer pre-rendered map from priceConfig when available
47+
if (self.options.priceConfig && self.options.priceConfig.installmentHtmlMap) {
48+
var map = self.options.priceConfig.installmentHtmlMap;
49+
if (typeof map[intAmount] !== 'undefined') {
50+
var wrapper = $('.price-installment_price');
51+
wrapper.html(map[intAmount]);
52+
wrapper.trigger('contentUpdated');
53+
$(document).trigger('installmentSlider');
54+
self.installmentCache[intAmount] = map[intAmount];
55+
return;
56+
}
57+
}
58+
59+
if (self.installmentCache[intAmount]) {
60+
var cachedHtml = self.installmentCache[intAmount];
61+
var wrapper = $('.price-installment_price');
62+
wrapper.html(cachedHtml);
63+
wrapper.trigger('contentUpdated');
64+
$(document).trigger('installmentSlider');
65+
return;
66+
}
67+
68+
self.options.ajax = $.ajax({
69+
type: 'get',
70+
url: url.build('/leanpay/installment/index/'),
71+
data: {"amount": amount},
72+
beforeSend: function () {
73+
if (typeof self.options.ajax !== 'undefined') {
74+
self.options.ajax.abort();
75+
}
76+
},
77+
success: function (response) {
78+
response = JSON.parse(response);
79+
if (typeof response.installment_html !== 'undefined') {
80+
var currentHtml = $('.price-installment_price .installment-wrapper');
81+
var newHtml = response.installment_html;
82+
var wrapper = $('.price-installment_price');
83+
84+
if (currentHtml !== newHtml) {
85+
wrapper.html(newHtml);
86+
wrapper.trigger('contentUpdated');
87+
$(document).trigger('installmentSlider');
88+
}
89+
90+
self.installmentCache[intAmount] = newHtml;
91+
}
92+
},
93+
cache: true,
94+
dataType: 'html'
95+
});
96+
},
2797
reloadPrice: function reDrawPrices()
2898
{
2999
var priceFormat = (this.options.priceConfig && this.options.priceConfig.priceFormat) || {},
30100
priceTemplate = mageTemplate(this.options.priceTemplate);
31101

102+
// Guard: only on PDP pages
103+
if (!$('body').hasClass('catalog-product-view')) {
104+
return;
105+
}
106+
32107
_.each(this.cache.displayPrices, function (price, priceCode) {
33108
price.final = _.reduce(price.adjustments, function (memo, amount) {
34109
return memo + amount;
@@ -38,6 +113,10 @@ define([
38113

39114
$(document).trigger('installment_price', [price.final]);
40115

116+
if (priceCode === 'finalPrice' && price.final > 0) {
117+
this.installmentPrice(price.final);
118+
}
119+
41120
$('[data-price-type="' + priceCode + '"]', this.element).html(priceTemplate({
42121
data: price
43122
}));

0 commit comments

Comments
 (0)