Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#33413: Creation Pages - optimize bundle items product data loading #336

Open
wants to merge 6 commits into
base: petsies-theme
Choose a base branch
from
4 changes: 3 additions & 1 deletion core/data-resolver/ProductService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@ const getProduct = async (options: { [key: string]: string }, key: string): Prom
size: 1,
configuration: { sku: options.childSku },
options: {
prefetchGroupProducts: true,
prefetchGroupProducts: options.prefetchGroupProducts !== undefined
? !!options.prefetchGroupProducts
: true,
assignProductConfiguration: true
}
})
Expand Down
15 changes: 13 additions & 2 deletions core/modules/catalog/store/product/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,17 @@ const actions: ActionTree<ProductState, RootState> = {
/**
* Load the product data and sets current product
*/
async loadProduct ({ dispatch, state }, { parentSku, childSku = null, route = null, skipCache = false, setCurrent = true }) {
async loadProduct (
{ dispatch, state },
{
prefetchGroupProducts = true,
parentSku,
childSku = null,
route = null,
skipCache = false,
setCurrent = true
}
) {
Logger.info('Fetching product data asynchronously', 'product', { parentSku, childSku })()

if (setCurrent) {
Expand All @@ -235,7 +245,8 @@ const actions: ActionTree<ProductState, RootState> = {
const product = await dispatch('single', {
options: {
sku: parentSku,
childSku: childSku
childSku: childSku,
prefetchGroupProducts: prefetchGroupProducts
},
key: 'sku',
skipCache
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,14 @@ export function useCustomizationsPrice (
const customizationOptionValuesLowestPrice = computed<Record<string, PriceHelper.ProductPrice | undefined>>(
() => {
const dictionary: Record<string, PriceHelper.ProductPrice | undefined> = {};
const productBySkuDictionary = root.$store.getters['product/getProductBySkuDictionary'];

customizations.value.forEach((customization) => {
if (!customization.optionData?.values) {
return;
}

dictionary[customization.id] = getLowestPriceForOptionValues(
customization.optionData.values,
productBySkuDictionary
customization.optionData.values
);
});

Expand All @@ -33,7 +31,6 @@ export function useCustomizationsPrice (
);

const totalPrice = computed<PriceHelper.ProductPrice>(() => {
const productBySkuDictionary = root.$store.getters['product/getProductBySkuDictionary'];
const selectedOptionValuesPrices: PriceHelper.ProductPrice[] = [];
const _customizationOptionValuesLowestPrice = customizationOptionValuesLowestPrice.value;

Expand Down Expand Up @@ -71,7 +68,7 @@ export function useCustomizationsPrice (
}

selectedValues.forEach((value) => {
const price = getOptionValuePrice(value, productBySkuDictionary);
const price = getOptionValuePrice(value);

if (price) {
selectedOptionValuesPrices.push(price);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@ export function useOptionValuesPrice (
const optionValuePriceDictionary = computed<Record<string, PriceHelper.ProductPrice | undefined>>(
() => {
const dictionary: Record<string, PriceHelper.ProductPrice | undefined> = {};
const productBySkuDictionary = root.$store.getters['product/getProductBySkuDictionary'];

// TODO: quick fix, need to refactor
const _ = root.$store.getters['promotionPlatform/campaignContent'];

values.value.forEach((optionValue) => {
dictionary[optionValue.id] = getOptionValuePrice(optionValue, productBySkuDictionary);
dictionary[optionValue.id] = getOptionValuePrice(optionValue);
});

return dictionary;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import Product from 'core/modules/catalog/types/Product';
import { PriceHelper } from 'src/modules/shared';

import { getOptionValuePrice } from './get-option-value-price';
import { OptionValue } from '../types/option-value.interface';

export function getLowestPriceForOptionValues (
optionValues: OptionValue[],
productBySkuDictionary: Record<string, Product>
optionValues: OptionValue[]
): PriceHelper.ProductPrice | undefined {
let lowestPrice: number | undefined;
let lowestProductPrice: PriceHelper.ProductPrice | undefined;

optionValues.forEach((optionValue) => {
const price = getOptionValuePrice(optionValue, productBySkuDictionary);
const price = getOptionValuePrice(optionValue);

if (!price) {
return;
Expand Down
51 changes: 30 additions & 21 deletions src/modules/customization-system/helpers/get-option-value-price.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,46 @@
import Product from '@vue-storefront/core/modules/catalog/types/Product';
import EventBus from '@vue-storefront/core/compatibility/plugins/event-bus'

import { PriceHelper } from 'src/modules/shared';
import UpdateProductDiscountPriceEventData from 'src/modules/shared/types/discount-price/update-product-discount-price-event-data.interface';
import { UPDATE_PRODUCT_DEFAULT_DISCOUNT_PRICE_DATA_EVENT_ID } from 'src/modules/shared/types/discount-price/events';

import { OptionValue } from '../types/option-value.interface';
import { getOptionValueSpecialPrice } from './get-option-value-special-price.function';

export function getOptionValuePrice (
optionValue: OptionValue,
productBySkuDictionary: Record<string, Product>
optionValue: OptionValue
): PriceHelper.ProductPrice | undefined {
const defaultOptionValuePrice = optionValue.price
? {
regular: optionValue.price,
special: null
}
: {
regular: 0,
special: null
}
const defaultPrice = {
regular: optionValue.price !== undefined
? optionValue.price
: 0,
special: getOptionValueSpecialPrice(optionValue)
}

if (!optionValue.sku) {
return defaultOptionValuePrice;
if (optionValue.productId === undefined) {
return defaultPrice;
}

const product = productBySkuDictionary[optionValue.sku];
const productDiscountPriceData: UpdateProductDiscountPriceEventData = {
value: undefined,
product: {
id: optionValue.productId
}
};

if (!product) {
return defaultOptionValuePrice;
}
EventBus.$emit(UPDATE_PRODUCT_DEFAULT_DISCOUNT_PRICE_DATA_EVENT_ID, productDiscountPriceData);

const price = PriceHelper.getProductDefaultPrice(product, {}, false);
if (productDiscountPriceData.value === undefined) {
return defaultPrice;
}

if (!price.regular) {
return defaultOptionValuePrice;
if (defaultPrice.special !== null && defaultPrice.special < productDiscountPriceData.value) {
return defaultPrice;
}

return price;
return {
regular: defaultPrice.regular,
special: productDiscountPriceData.value
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { OptionValue } from '../types/option-value.interface';

function isSpecialPriceAvailable (
optionValue: OptionValue
): boolean {
if (!optionValue.specialPriceFromDate && !optionValue.specialPriceToDate) {
return true;
}

const now = new Date()
const fromDate = optionValue.specialPriceFromDate ? new Date(optionValue.specialPriceFromDate) : undefined
const toDate = optionValue.specialPriceToDate ? new Date(optionValue.specialPriceToDate) : undefined

const isFromDateAvailable = !fromDate || fromDate < now;
const isToDateAvailable = !toDate || toDate > now;

return isFromDateAvailable && isToDateAvailable;
}

export function getOptionValueSpecialPrice (optionValue: OptionValue): number | null {
if (optionValue.specialPrice === null || optionValue.specialPrice === undefined) {
return null;
}

if (!isSpecialPriceAvailable(optionValue)) {
return null;
}

return optionValue.specialPrice;
}
2 changes: 2 additions & 0 deletions src/modules/customization-system/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { getCartItemExtensionAttributes } from './helpers/get-cart-item-extensio
import { getCustomizationSelectedValues } from './helpers/get-customization-selected-values';
import { getCustomizationSystemCartItemThumbnail } from './helpers/get-customization-system-cart-item-thumbnail';
import { getCustomizationValueIdFieldKey } from './helpers/get-customization-value-id-field-key';
import { getOptionValueSpecialPrice } from './helpers/get-option-value-special-price.function';
import { getSelectedOptionValuesByCustomizationState } from './helpers/get-selected-options-values-by-customization-state';
import { isEmailCustomization } from './helpers/is-email-customization';
import { requiredCustomizationsFilter } from './helpers/required-customizations-filter';
Expand Down Expand Up @@ -69,6 +70,7 @@ export {
getCustomizationSelectedValues,
getCustomizationSystemCartItemThumbnail,
getCustomizationValueIdFieldKey,
getOptionValueSpecialPrice,
getSelectedOptionValuesByCustomizationState,
isEmailCustomization,
isFileUploadValue,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,9 @@ export interface OptionValue {
availabilityRules?: AvailabilityRules,
actions?: Actions,
sn: number,
galleryImages?: GalleryImage[]
galleryImages?: GalleryImage[],
specialPrice?: number,
specialPriceFromDate?: string,
specialPriceToDate?: string,
productId?: number
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { BundleOptionsProductLink, SelectedBundleOption } from '@vue-storefront/
import { getBundleOptionsValues, getDefaultBundleOptions } from '@vue-storefront/core/modules/catalog/helpers/bundleOptions';
import Product from '@vue-storefront/core/modules/catalog/types/Product';
import RootState from '@vue-storefront/core/types/RootState'
import { getOptionValueSpecialPrice, getSelectedOptionValuesByCustomizationState, OptionValue } from 'src/modules/customization-system';
import CartItem from '@vue-storefront/core/modules/cart/types/CartItem';

function getBundleOptionDiscountPrice (bundleOptionValues: BundleOptionsProductLink[], store: Store<RootState>): number | undefined {
let isDiscounted = false;
Expand Down Expand Up @@ -31,15 +33,74 @@ function getBundleOptionDiscountPrice (bundleOptionValues: BundleOptionsProductL
return price;
}

export function getBundleCartItemDiscountPrice (product: Product, store: Store<RootState>): number | undefined {
const allBundleOptions = product.bundle_options || [];
function getOptionValuesDiscountPrice (
optionValues: OptionValue[],
store: Store<RootState>
): number | undefined {
let isDiscounted = false;
let price = 0;

for (const optionValue of optionValues) {
if (!optionValue.price || !optionValue.productId) {
continue;
}

const productPrice = store.getters['promotionPlatform/getProductCampaignDiscountPrice'](
{
id: optionValue.productId
}
);
const optionValueSpecialPrice = getOptionValueSpecialPrice(optionValue);

if (productPrice !== undefined) {
isDiscounted = true;
price += productPrice;
continue;
}

if (optionValueSpecialPrice !== null) {
price += optionValueSpecialPrice;
continue;
}

price += optionValue.price || 0;
}

const selectedBundleOptions = Object.values(get(product, 'product_option.extension_attributes.bundle_options', {}));
if (!isDiscounted) {
return;
}

return price;
}

function getBundleCartItemWithoutCustomizationsDiscountPrice (
cartItem: CartItem,
store: Store<RootState>
): number | undefined {
const allBundleOptions = cartItem.bundle_options || [];

const selectedBundleOptions = Object.values(get(cartItem, 'product_option.extension_attributes.bundle_options', {}));
const bundleOptionsValues = getBundleOptionsValues(selectedBundleOptions as SelectedBundleOption[], allBundleOptions);

return getBundleOptionDiscountPrice(bundleOptionsValues, store);
}

export function getBundleCartItemDiscountPrice (
cartItem: CartItem,
store: Store<RootState>
): number | undefined {
if (!cartItem.customizations || !cartItem.extension_attributes?.customization_state) {
return getBundleCartItemWithoutCustomizationsDiscountPrice(cartItem, store);
}

const selectedOptionValues = getSelectedOptionValuesByCustomizationState(
cartItem.extension_attributes.customization_state,
cartItem.customizations
);

return getOptionValuesDiscountPrice(selectedOptionValues, store);
}

export function getBundleProductDefaultDiscountPrice (product: Product, store: Store<RootState>): number | undefined {
const allBundleOptions = product.bundle_options || [];

Expand Down
Loading