Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
66 changes: 65 additions & 1 deletion ViewModel/Warranty.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
use Extend\Warranty\Model\Config\Source\ProductPagePlacement;
use Magento\Backend\Model\Auth\Session as AdminSession;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\ResourceModel\Category\Collection;
use Magento\Catalog\Pricing\Price\FinalPrice;
use Magento\Checkout\Model\Session as CheckoutSession;
use Magento\ConfigurableProduct\Api\LinkManagementInterface;
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
Expand Down Expand Up @@ -649,7 +651,7 @@ public function showLeadOffer($orderItem)

public function getProductInfo($product)
{
$price = $product->getFinalPrice();
$price = $this->getOfferPrice($product);

/** @var Collection $categoryCollection */
$categoryCollection = $product->getCategoryCollection();
Expand All @@ -666,6 +668,68 @@ public function getProductInfo($product)
];
}

/**
* Retrieve the price the storefront renders for the product
*
* The price info registry resolves special prices, catalog price rules and
* customer group / tier prices for the current session, which is what the PDP
* price block displays. Product::getFinalPrice() without a qty argument skips
* tier prices entirely - and customer group prices are stored as tier price
* rows - so it reports the base or special price to Extend instead.
*
* getValue() is used rather than getAmount()->getValue() to keep the price
* excluding tax, consistent with the rest of the offer payload.
*
* @param ProductInterface $product
* @return float
*/
public function getOfferPrice($product): float
{
try {
if (method_exists($product, 'getPriceInfo')) {
$finalPrice = $product->getPriceInfo()->getPrice(FinalPrice::PRICE_CODE);
if ($finalPrice) {
return (float)$finalPrice->getValue();
}
}
} catch (Exception $e) {
// price info is unavailable for this product, fall back to the price model
}

// the qty argument is required, otherwise tier and group prices are ignored
return (float)$product->getFinalPrice(1);
}

/**
* Offer prices of each variation of a configurable product, keyed by child SKU
*
* Passed to the PDP widget so the offer can be re-priced when the shopper
* switches variation - children carry their own special and customer group
* prices, and the parent price is only the lowest of them.
*
*
* @param ProductInterface $product
* @return string
*/
public function getVariationPricesJson($product): string
{
$prices = [];

if ($product instanceof Product && $product->getTypeId() === Configurable::TYPE_CODE) {
$typeInstance = $product->getTypeInstance();

if ($typeInstance instanceof Configurable) {
foreach ($typeInstance->getSalableUsedProducts($product) as $childProduct) {
$prices[$childProduct->getSku()] = $this->helper->formatPrice(
$this->getOfferPrice($childProduct)
);
}
}
}

return $this->jsonSerializer->serialize($prices);
}

public function getOrderIncrementId(string|int|null $orderId = null){
if ($orderId){
$order = $this->orderRepository->get($orderId);
Expand Down
3 changes: 3 additions & 0 deletions view/frontend/templates/product/list/warranty-offers.phtml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ $isConfigurable = $productType === \Magento\ConfigurableProduct\Model\Product\Ty
"productSku": "<?= $isSimple ? $block->escapeJs($_product->getData('sku')) : '' ?>",
"buttonEnabled": false,
"productInfo": <?= json_encode($viewModel->getProductInfo($_product)); ?>,
<?php if ($isConfigurable): ?>
"variationPrices": <?= $viewModel->getVariationPricesJson($_product); ?>,
<?php endif; ?>
"modalEnabled": true,
"insertionPoint": "div.product-item-actions",
"insertionLogic": "append",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ $product = $block->getProduct();
"productId": "<?= $block->escapeJs($product->getId()) ?>",
"productSku": "warranties_are_only_for_simples",
"productInfo": <?= json_encode($viewModel->getProductInfo($product)); ?>,
"variationPrices": <?= $viewModel->getVariationPricesJson($product); ?>,
"buttonEnabled": <?= $block->escapeJs($viewModel->isProductDetailPageOffersEnabled() ? 1 : 0) ?>,
"modalEnabled": <?= $block->escapeJs($viewModel->isInterstitialCartOffersEnabled() ? 1 : 0) ?>,
"insertionPoint": "<?= $block->escapeJs($placement['insertionPoint']) ?>",
Expand Down
64 changes: 57 additions & 7 deletions view/frontend/web/js/product/configurable-warranty.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ define([
productSku: null,
buttonEnabled: true,
modalEnabled: false,
variationPrices: {},
blockClass: 'product-warranty-offers',
insertionPoint: 'div.actions',
insertionLogic: 'before',
Expand Down Expand Up @@ -52,11 +53,44 @@ define([
* @param {Event} event - The event arguments
*/
_onOptionsChanged: function (event) {
if (!this.options.buttonEnabled)
if (!this.options.buttonEnabled && !this.options.modalEnabled)
return;

var productSku = this._getWarrantyProductSku();
this.warrantyBlock.extendWarrantyOffers('updateActiveProduct', productSku);

// the parent SKU is not rendered for configurable products on the PLP, so a
// non-swatch selection leaves us without a SKU to price the offer with
if (!productSku && !this.options.isInProductView) {
productSku = this._getSelectedConfigurableSku();
}

var price = this._getVariationPrice(productSku);

// keeps the interstitial modal on the price of the selected variation
if (price !== null) {
this.options.productInfo.price = price;
}

if (this.options.buttonEnabled) {
this.warrantyBlock.extendWarrantyOffers('updateActiveProduct', productSku, price);
Comment thread
Johnathnnault marked this conversation as resolved.
}
},

/**
* Returns the offer price of the given variation, `null` when it is unknown
*
* Variation prices are rendered server side, so special, catalog rule and
* customer group prices of the child product are all taken into account.
*
*
* @protected
* @param {String} productSku
* @return {Number|null}
*/
_getVariationPrice: function (productSku) {
var prices = this.options.variationPrices || {};

return productSku && prices.hasOwnProperty(productSku) ? prices[productSku] : null;
},

/**
Expand All @@ -82,14 +116,30 @@ define([
}
}
} else if (this.options.isInProductView) {
var selectedId = $('input[name=selected_configurable_option]', this.mainWrap).val();
if (selectedId && selectedId !== '') {
var spConfig = this.addToCartForm.data('mageConfigurable').options.spConfig;
selectedSku = spConfig && spConfig.skus ? spConfig.skus[selectedId] : null;
}
selectedSku = this._getSelectedConfigurableSku();
}

return selectedSku ? selectedSku : this.options.productSku;
},

/**
* Returns the SKU of the currently selected configurable option, `null` when
* nothing is selected or the configurable widget is unavailable
*
* @protected
* @return {String|null}
*/
_getSelectedConfigurableSku: function () {
var selectedId = $('input[name=selected_configurable_option]', this.mainWrap).val();

if (!selectedId || selectedId === '') {
return null;
}

var configurable = this.addToCartForm.data('mageConfigurable');
var spConfig = configurable ? configurable.options.spConfig : null;

return spConfig && spConfig.skus && spConfig.skus[selectedId] ? spConfig.skus[selectedId] : null;
}
});

Expand Down
10 changes: 7 additions & 3 deletions view/frontend/web/js/warranty-offers-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,21 @@ define([
* Updates warranty offers product
*
* @param {String} productSku - new product SKU
* @param {Number|null} price - price of the new product, the price of the
* currently active product is kept when omitted
*/
updateActiveProduct: function (productSku) {
updateActiveProduct: function (productSku, price) {
var component = this.getButtonInstance();
if (!component)
return;

var product = component.getActiveProduct() || { id: '' };
if (product.id !== productSku) {
var newPrice = (price === undefined || price === null) ? product.price : price;

if (product.id !== productSku || product.price !== newPrice) {
let activeProduct = {
referenceId:productSku,
price: product.price,
price: newPrice,
category: this.sanitizeValue(product.category)
};
component.setActiveProduct(activeProduct);
Expand Down