From e5c15514db7645a30c6721f0053c425d434bf355 Mon Sep 17 00:00:00 2001 From: Ilya Litvinenko Date: Thu, 4 Feb 2021 16:44:00 +0200 Subject: [PATCH] VDS-800: Integrate configurable products backend with theme (#173) * Integrate configurable backend with product page * Revert change for storefrontApp variable * Integrate configurable backend to checkout page * Integrate configurable backend to order detail page * Integrate configurable backend to configurable product card * Add missing dependencies * Fix custom part icon case * Fix console error on empty part items * Set default price 0 for unavailable configured product * Fix wrong default price for configurables * Hide panel if there's no parts --- .../checkout-configurable-line-item.js | 7 +- ...out-configurable-line-item.tpl.html.liquid | 21 ++--- assets/js/common-components/lineItems.js | 3 - .../js/common-components/lineItems.tpl.liquid | 14 +-- .../product-card/configurable-product-card.js | 36 +++++-- .../product/configurable-product.js | 93 +++++++++++-------- .../product/product-configuration-list.liquid | 6 +- .../product/type/configured/item-price.liquid | 2 +- .../product/type/configured/product.liquid | 2 +- templates/product.liquid | 2 +- 10 files changed, 106 insertions(+), 80 deletions(-) diff --git a/assets/js/checkout/checkout-configurable-line-item.js b/assets/js/checkout/checkout-configurable-line-item.js index 92f061f0..168ac85c 100644 --- a/assets/js/checkout/checkout-configurable-line-item.js +++ b/assets/js/checkout/checkout-configurable-line-item.js @@ -56,10 +56,9 @@ storefrontApp.component('vcCheckoutConfigurableLineItem', { }, true); function getConfiguredLineItems(item) { - _.each(item.parts, part => { - part.items = [item.items.find(x => x.id === part.selectedItemId)]; - if (part.items[0].validationErrors && part.items[0].validationErrors.length) { - part.quantityError = _.find(part.items[0].validationErrors, error => error.errorCode === "QuantityError"); + _.each(item.items, part => { + if (part.validationErrors && part.validationErrors.length) { + part.quantityError = _.find(part.validationErrors, error => error.errorCode === "QuantityError"); if (part.quantityError && part.quantityError.availableQuantity === 0) { $scope.outOfStockError = true; } diff --git a/assets/js/checkout/checkout-configurable-line-item.tpl.html.liquid b/assets/js/checkout/checkout-configurable-line-item.tpl.html.liquid index a9fcc7e9..b686f4ac 100644 --- a/assets/js/checkout/checkout-configurable-line-item.tpl.html.liquid +++ b/assets/js/checkout/checkout-configurable-line-item.tpl.html.liquid @@ -69,33 +69,32 @@ - - - {% raw %}{{ part.items[0].name }}{% endraw %} + + + {% raw %}{{ part.name }}{% endraw %} - - - + +

{{ 'checkout.price' | t }} {% if settings.show_prices_with_taxes %} - + {% else %} - + {% endif %} / each

- + {{ 'checkout.out_of_stock' | t }} {% if settings.show_prices_with_taxes %} - + {% else %} - + {% endif %} diff --git a/assets/js/common-components/lineItems.js b/assets/js/common-components/lineItems.js index f973c3fe..84b8c568 100644 --- a/assets/js/common-components/lineItems.js +++ b/assets/js/common-components/lineItems.js @@ -37,9 +37,6 @@ storefrontApp.component('vcLineItems', { function getConfiguredLineItems(groups) { _.each(groups, group => { angular.extend(group, { showConfiguration: false }); - _.each(group.parts, part => { - part.items = [group.items.find(x => x.id === part.selectedItemId)]; - }); }); } diff --git a/assets/js/common-components/lineItems.tpl.liquid b/assets/js/common-components/lineItems.tpl.liquid index a06f5a3f..a681ac90 100644 --- a/assets/js/common-components/lineItems.tpl.liquid +++ b/assets/js/common-components/lineItems.tpl.liquid @@ -59,18 +59,18 @@ - - - {% raw %}{{ part.items[0].name }}{% endraw %} + + + {% raw %}{{ part.name }}{% endraw %} - + - + {% if settings.show_prices_with_taxes %} - + {% else %} - + {% endif %} diff --git a/assets/js/controllers/product-card/configurable-product-card.js b/assets/js/controllers/product-card/configurable-product-card.js index e5250e3d..9cb30ab6 100644 --- a/assets/js/controllers/product-card/configurable-product-card.js +++ b/assets/js/controllers/product-card/configurable-product-card.js @@ -1,22 +1,40 @@ var storefrontApp = angular.module('storefrontApp'); -storefrontApp.controller('configurableProductCardController', ['$scope', 'catalogService', '$filter', 'roundHelper', 'storeCurrency', - function ($scope, catalogService, $filter, roundHelper, storeCurrency) { +storefrontApp.controller('configurableProductCardController', ['$scope', 'catalogService', '$filter', 'storeCurrency', 'pricingService', + function ($scope, catalogService, $filter, storeCurrency, pricingService) { + $scope.isProductUnavailable = false; $scope.getDefaultPrice = function() { return $filter('currency')($scope.defaultPrice, storeCurrency.symbol); } $scope.initProductConfiguration = function(productId) { - catalogService.getProductConfiguration(productId).then(function(response) { - $scope.productParts = response.data; - $scope.defaultProductParts = []; - _.each($scope.productParts, function (part) { - $scope.defaultProductParts.push(part.items.find(x => x.id === part.selectedItemId)); + catalogService.getProduct([productId]).then(response => { + let defaultPartsTotalsObject = []; + $scope.productParts = response.data[0].parts; + + if ($scope.productParts.length) { + _.each($scope.productParts, part => { + if (!part.items || !part.items.length) { + $scope.isProductUnavailable = true; + return; + } + defaultPartsTotalsObject.push({id: part.items.find(x => x.id === part.selectedItemId).id, quantity: 1}); }); - $scope.defaultPrice = roundHelper.bankersRound($scope.defaultProductParts.reduce((prev, cur) => prev + cur.price.actualPrice.amount, 0)); - }); + if (!$scope.isProductUnavailable) { + pricingService.getProductsTotal(defaultPartsTotalsObject).then(result => { + $scope.defaultPrice = $scope.showPricesWithTaxes ? result.data.totalWithTax.amount : result.data.total.amount; + }); + } else { + $scope.defaultPrice = 0; + } + + } else { + $scope.defaultPrice = 0; + } + + }); } }]); diff --git a/assets/js/controllers/product/configurable-product.js b/assets/js/controllers/product/configurable-product.js index 760f0879..aa41b895 100644 --- a/assets/js/controllers/product/configurable-product.js +++ b/assets/js/controllers/product/configurable-product.js @@ -1,29 +1,26 @@ var storefrontApp = angular.module('storefrontApp'); -storefrontApp.controller('configurableProductController', ['$rootScope', '$scope', '$window', 'dialogService', 'catalogService', 'cartService', '$filter', 'roundHelper', 'availabilityService', 'storeCurrency', - function ($rootScope, $scope, $window, dialogService, catalogService, cartService, $filter, roundHelper, availabilityService, storeCurrency) { +storefrontApp.controller('configurableProductController', ['$rootScope', '$scope', '$window', 'dialogService', 'catalogService', 'cartService', '$filter', 'roundHelper', 'availabilityService', 'storeCurrency', 'pricingService', + function ($rootScope, $scope, $window, dialogService, catalogService, cartService, $filter, roundHelper, availabilityService, storeCurrency, pricingService) { $scope.configurationQty = 1; + $scope.isProductUnavailable = false; $scope.addSelectedProductsToCart = function() { - var configuredProductId = $window.product.id; - var products = $scope.productParts.map(function(part) { - return part.items.find(function(item) { - return item.id === part.selectedItemId; - }); - }); - var inventoryError = products.some(product => { - return product.availableQuantity < $scope.configurationQty; + const configuredProductId = $window.product.id; + const products = $scope.productParts.map(part => { + return part.items.find(item => item.id === part.selectedItemId); }); - var dialogData = toDialogDataModel(products, $scope.configurationQty, inventoryError, configuredProductId); + const inventoryError = products.some(product => product.availableQuantity < $scope.configurationQty); + const dialogData = toDialogDataModel(products, $scope.configurationQty, inventoryError, configuredProductId); dialogService.showDialog(dialogData, 'recentlyAddedCartItemDialogController', 'storefront.recently-added-cart-item-dialog.tpl', 'lg'); if (!inventoryError) { - var items = $scope.productParts.map(function(value) { + const items = $scope.productParts.map(value => { return { id: value.selectedItemId, quantity: $scope.configurationQty, configuredProductId: configuredProductId }; }); - cartService.addLineItems(items).then(function (response) { - var result = response.data; + cartService.addLineItems(items).then(response => { + const result = response.data; if (result.isSuccess) { $rootScope.$broadcast('cartItemsChanged'); } @@ -32,8 +29,8 @@ storefrontApp.controller('configurableProductController', ['$rootScope', '$scope } $scope.changeGroupItem = function (productPart) { - var dialogInstance = dialogService.showDialog(productPart, 'changeConfigurationGroupItemDialogController', 'storefront.select-configuration-item-dialog.tpl'); - dialogInstance.result.then(function (id) { + const dialogInstance = dialogService.showDialog(productPart, 'changeConfigurationGroupItemDialogController', 'storefront.select-configuration-item-dialog.tpl'); + dialogInstance.result.then(id => { const foundIndex = $scope.productParts.findIndex(x => x.name === productPart.name); $scope.productParts[foundIndex].selectedItemId = id; recalculateTotals(); @@ -46,7 +43,7 @@ storefrontApp.controller('configurableProductController', ['$rootScope', '$scope } $scope.getCurrentTotal = function() { - var total; + let total; if ($scope.updatedTotal) { total = roundHelper.bankersRound($scope.updatedTotal * $scope.configurationQty); @@ -76,50 +73,66 @@ storefrontApp.controller('configurableProductController', ['$rootScope', '$scope } function toDialogDataModel(products, quantity, inventoryError, configuredProductId) { - let productIds = products.map(function(product) { + const productIds = products.map(product => { return product.id; }); - let items = products.map(function(product) { + const items = products.map(product => { return angular.extend({ }, product, { quantity: +quantity, inventoryError: product.availableQuantity < quantity, configuredProductId: configuredProductId }) }); return { productIds, items, inventoryError, configuredProductId, configurationQty: quantity }; } function initialize() { - var product = $window.product; + let product = $window.product; if (!product) { return; } - catalogService.getProduct([product.id]).then(function (response) { + catalogService.getProduct([product.id]).then(response => { + let defaultPartsTotalsObject = []; product = response.data[0]; $scope.selectedVariation = product; + $scope.productParts = response.data[0].parts; + $scope.defaultProductParts = []; + + if ($scope.productParts.length) { + _.each($scope.productParts, part => { + if (!part.items || !part.items.length) { + $scope.isProductUnavailable = true; + return; + } + $scope.defaultProductParts.push(part.items.find(x => x.id === part.selectedItemId)); + defaultPartsTotalsObject.push({id: part.items.find(x => x.id === part.selectedItemId).id, quantity: 1}); + }); + + if (!$scope.isProductUnavailable) { + pricingService.getProductsTotal(defaultPartsTotalsObject).then(result => { + $scope.defaultPrice = $scope.showPricesWithTaxes ? result.data.totalWithTax.amount : result.data.total.amount; + }); + } else { + $scope.defaultPrice = 0; + } + + } else { + $scope.defaultPrice = 0; + } - return availabilityService.getProductsAvailability([product.id]).then(function(res) { + + return availabilityService.getProductsAvailability([product.id]).then(res => { $scope.availability = _.object(_.pluck(res.data, 'productId'), res.data); }); }); } function recalculateTotals() { - $scope.selectedProductParts = []; - _.each($scope.productParts, function (part) { - $scope.selectedProductParts.push(part.items.find(x => x.id === part.selectedItemId)); + let selectedProductParts = []; + _.each($scope.productParts, part => { + selectedProductParts.push({id: part.items.find(x => x.id === part.selectedItemId).id, quantity: 1}); }); - $scope.updatedTotal = roundHelper.bankersRound($scope.selectedProductParts.reduce((prev, cur) => prev + cur.price.actualPrice.amount, 0)); - $scope.totalDifference = roundHelper.bankersRound(Math.abs($scope.updatedTotal - $scope.defaultPrice)); - $scope.differenceSign = ($scope.updatedTotal === $scope.defaultPrice) ? '' : - ($scope.updatedTotal > $scope.defaultPrice) ? '+' : '-'; - } - - $scope.initProductConfiguration = function(productId) { - catalogService.getProductConfiguration(productId).then(function(response) { - $scope.productParts = response.data; - $scope.defaultProductParts = []; - _.each($scope.productParts, function (part) { - $scope.defaultProductParts.push(part.items.find(x => x.id === part.selectedItemId)); - }); - - $scope.defaultPrice = roundHelper.bankersRound($scope.defaultProductParts.reduce((prev, cur) => prev + cur.price.actualPrice.amount, 0)); + pricingService.getProductsTotal(selectedProductParts).then(result => { + $scope.updatedTotal = $scope.showPricesWithTaxes ? result.data.totalWithTax.amount : result.data.total.amount; + $scope.totalDifference = Math.abs($scope.updatedTotal - $scope.defaultPrice); + $scope.differenceSign = ($scope.updatedTotal === $scope.defaultPrice) ? '' : + ($scope.updatedTotal > $scope.defaultPrice) ? '+' : '-'; }); } diff --git a/snippets/product/product-configuration-list.liquid b/snippets/product/product-configuration-list.liquid index b4f5f05d..c06ba7b8 100644 --- a/snippets/product/product-configuration-list.liquid +++ b/snippets/product/product-configuration-list.liquid @@ -1,5 +1,5 @@ {% if product.product_type == "Configurable" %} -
+
@@ -8,8 +8,8 @@
-
- {% raw %}{{ part.name }}{% endraw %} +
+ {% raw %}{{ part.name }}{% endraw %} {% raw %}{{ part.name }}{% endraw %}
diff --git a/snippets/product/type/configured/item-price.liquid b/snippets/product/type/configured/item-price.liquid index d866b5b9..ce4efd88 100644 --- a/snippets/product/type/configured/item-price.liquid +++ b/snippets/product/type/configured/item-price.liquid @@ -10,7 +10,7 @@ -
+
diff --git a/snippets/product/type/configured/product.liquid b/snippets/product/type/configured/product.liquid index 2ce26358..884e7eee 100644 --- a/snippets/product/type/configured/product.liquid +++ b/snippets/product/type/configured/product.liquid @@ -1,5 +1,5 @@ {% include 'product/type/common/product-title-info' %} -
+
{% include 'product/image' %} {% include 'product/compare' %} diff --git a/templates/product.liquid b/templates/product.liquid index 7f2746f7..fca436a4 100644 --- a/templates/product.liquid +++ b/templates/product.liquid @@ -24,7 +24,7 @@ {% if product.product_type == "Configurable" %} - + {% include 'product/type/configured/product' %} {% elsif variants_size != 1 %}