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
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.12.0-rc1] - UNRELEASED
## [1.12.0] - 2020.06.01

### Added

Expand Down Expand Up @@ -62,6 +62,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Bugfix for reactivity of `current_configuration` in `populateProductConfigurationAsync` - @cewald (#4258)
- Bugfix for build exception in Node v13.13+ - @cewald (#4249)
- Convert option ids to string while comparing them in `getProductConfiguration` - @gibkigonzo (#4484)
- change value to number in price filter - @gibkigonzo (#4478)

### Changed / Improved

Expand All @@ -80,6 +81,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Homepage, new products query, uses now `new` attribute - @mdanilwoicz
- Refactor product module, more info in upgrade notes- @gibkigonzo (#3952, #4459)
- Move default theme to separate repository https://github.com/DivanteLtd/vsf-default - @gibkigonzo (#4255)
- add two numbers after dot to price by default, calculate default price for bundle or grouped main product, update typing, add fallback to attribute options - @gibkigonzo (#4476)
- udpate yarn and filter shipping methods for instant checkout - @gibkigonzo (#4480)

## [1.11.4] - 2020.05.26

Expand Down
2 changes: 1 addition & 1 deletion core/i18n/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue-storefront/i18n",
"version": "1.11.4",
"version": "1.12.0",
"description": "Vue Storefront i18n",
"license": "MIT",
"main": "index.ts",
Expand Down
47 changes: 5 additions & 42 deletions core/modules/catalog-next/store/category/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,17 @@ import * as types from './mutation-types'
import RootState from '@vue-storefront/core/types/RootState'
import CategoryState from './CategoryState'
import { quickSearchByQuery } from '@vue-storefront/core/lib/search'
import { buildFilterProductsQuery, isServer } from '@vue-storefront/core/helpers'
import { buildFilterProductsQuery } from '@vue-storefront/core/helpers'
import { router } from '@vue-storefront/core/app'
import { currentStoreView, localizedDispatcherRoute, localizedDispatcherRouteName } from '@vue-storefront/core/lib/multistore'
import { localizedDispatcherRoute } from '@vue-storefront/core/lib/multistore'
import FilterVariant from '../../types/FilterVariant'
import { CategoryService } from '@vue-storefront/core/data-resolver'
import { changeFilterQuery } from '../../helpers/filterHelpers'
import { products, entities } from 'config'
import { configureProductAsync } from '@vue-storefront/core/modules/catalog/helpers'
import { DataResolver } from 'core/data-resolver/types/DataResolver';
import { Category } from '../../types/Category';
import { _prepareCategoryPathIds } from '../../helpers/categoryHelpers';
import { prefetchStockItems } from '../../helpers/cacheProductsHelper';
import { preConfigureProduct } from '@vue-storefront/core/modules/catalog/helpers/search'
import chunk from 'lodash-es/chunk'
import omit from 'lodash-es/omit'
import cloneDeep from 'lodash-es/cloneDeep'
Expand Down Expand Up @@ -121,43 +119,6 @@ const actions: ActionTree<CategoryState, RootState> = {
}
}
},
/**
* Calculates products taxes
* Registers URLs
* Configures products
*/
async processCategoryProducts ({ dispatch, rootState }, { products = [], filters = {} } = {}) {
dispatch('registerCategoryProductsMapping', products) // we don't need to wait for this
const configuredProducts = await dispatch('configureProducts', { products, filters })
return dispatch('tax/calculateTaxes', { products: configuredProducts }, { root: true })
},
/**
* Configure configurable products to have first available options selected
* so they can be added to cart/wishlist/compare without manual configuring
*/
async configureProducts ({ rootState }, { products = [], filters = {}, populateRequestCacheTags = config.server.useOutputCacheTagging } = {}) {
return products.map(product => {
product = Object.assign({}, preConfigureProduct({ product, populateRequestCacheTags }))
const configuredProductVariant = configureProductAsync({ rootState, state: { current_configuration: {} } }, { product, configuration: filters, selectDefaultVariant: false, fallbackToDefaultWhenNoAvailable: true, setProductErorrs: false })
return Object.assign(product, omit(configuredProductVariant, ['visibility']))
})
},
async registerCategoryProductsMapping ({ dispatch }, products = []) {
const { storeCode, appendStoreCode } = currentStoreView()
await Promise.all(products.map(product => {
const { url_path, sku, slug, type_id } = product
return dispatch('url/registerMapping', {
url: localizedDispatcherRoute(url_path, storeCode),
routeData: {
params: {
parentSku: product.parentSku || product.sku,
slug
},
'name': localizedDispatcherRouteName(type_id + '-product', storeCode, appendStoreCode)
}
}, { root: true })
}))
},
async findCategories (context, categorySearchOptions: DataResolver.CategorySearchOptions): Promise<Category[]> {
return CategoryService.getCategories(categorySearchOptions)
},
Expand Down Expand Up @@ -299,7 +260,9 @@ const actions: ActionTree<CategoryState, RootState> = {
}, { root: true })
}
}
}
},
/** Below actions are not used from 1.12 and can be removed to reduce bundle */
...require('./deprecatedActions').default
}

export default actions
47 changes: 47 additions & 0 deletions core/modules/catalog-next/store/category/deprecatedActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { currentStoreView, localizedDispatcherRoute, localizedDispatcherRouteName } from '@vue-storefront/core/lib/multistore'
import { preConfigureProduct } from '@vue-storefront/core/modules/catalog/helpers/search'
import omit from 'lodash-es/omit'
import config from 'config'
const { configureProductAsync } = require('@vue-storefront/core/modules/catalog/helpers')

const actions = {
/**
* Calculates products taxes
* Registers URLs
* Configures products
*/
async processCategoryProducts ({ dispatch, rootState }, { products = [], filters = {} } = {}) {
dispatch('registerCategoryProductsMapping', products) // we don't need to wait for this
const configuredProducts = await dispatch('configureProducts', { products, filters })
return dispatch('tax/calculateTaxes', { products: configuredProducts }, { root: true })
},
/**
* Configure configurable products to have first available options selected
* so they can be added to cart/wishlist/compare without manual configuring
*/
async configureProducts ({ rootState }, { products = [], filters = {}, populateRequestCacheTags = config.server.useOutputCacheTagging } = {}) {
return products.map(product => {
product = Object.assign({}, preConfigureProduct({ product, populateRequestCacheTags }))
const configuredProductVariant = configureProductAsync({ rootState, state: { current_configuration: {} } }, { product, configuration: filters, selectDefaultVariant: false, fallbackToDefaultWhenNoAvailable: true, setProductErorrs: false })
return Object.assign(product, omit(configuredProductVariant, ['visibility']))
})
},
async registerCategoryProductsMapping ({ dispatch }, products = []) {
const { storeCode, appendStoreCode } = currentStoreView()
await Promise.all(products.map(product => {
const { url_path, sku, slug, type_id } = product
return dispatch('url/registerMapping', {
url: localizedDispatcherRoute(url_path, storeCode),
routeData: {
params: {
parentSku: product.parentSku || product.sku,
slug
},
'name': localizedDispatcherRouteName(type_id + '-product', storeCode, appendStoreCode)
}
}, { root: true })
}))
}
}

export default actions
145 changes: 145 additions & 0 deletions core/modules/catalog/helpers/deprecatedHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import Vue from 'vue'
import EventBus from '@vue-storefront/core/compatibility/plugins/event-bus'
import omit from 'lodash-es/omit'
import i18n from '@vue-storefront/i18n'
import { Logger } from '@vue-storefront/core/lib/logger'
import { optionLabel } from './optionLabel'
import { setProductConfigurableOptions } from './productOptions'
import config from 'config'
import { findConfigurableVariant } from './variant'
import { hasImage } from './'

export function populateProductConfigurationAsync (context, { product, selectedVariant }) {
Logger.warn('deprecated, will be not used from 1.12')()
if (product.configurable_options) {
for (let option of product.configurable_options) {
let attribute_code
let attribute_label
if (option.attribute_code) {
attribute_code = option.attribute_code
attribute_label = option.label ? option.label : (option.frontend_label ? option.frontend_label : option.default_frontend_label)
} else {
if (option.attribute_id) {
let attr = context.rootState.attribute.list_by_id[option.attribute_id]
if (!attr) {
Logger.error('Wrong attribute given in configurable_options - can not find by attribute_id', option)()
continue
} else {
attribute_code = attr.attribute_code
attribute_label = attr.frontend_label ? attr.frontend_label : attr.default_frontend_label
}
} else {
Logger.error('Wrong attribute given in configurable_options - no attribute_code / attribute_id', option)()
}
}
let selectedOption = null
if (selectedVariant.custom_attributes) {
selectedOption = selectedVariant.custom_attributes.find((a) => { // this is without the "label"
return (a.attribute_code === attribute_code)
})
} else {
selectedOption = {
attribute_code: attribute_code,
value: selectedVariant[attribute_code]
}
}
if (option.values && option.values.length) {
const selectedOptionMeta = option.values.find(ov => { return ov.value_index === selectedOption.value })
if (selectedOptionMeta) {
selectedOption.label = selectedOptionMeta.label ? selectedOptionMeta.label : selectedOptionMeta.default_label
selectedOption.value_data = selectedOptionMeta.value_data
}
}

const confVal = {
attribute_code: attribute_code,
id: selectedOption.value,
label: selectedOption.label ? selectedOption.label : /* if not set - find by attribute */optionLabel(context.rootState.attribute, { attributeKey: selectedOption.attribute_code, searchBy: 'code', optionId: selectedOption.value })
}
Vue.set(context.state.current_configuration, attribute_code, confVal)
}
setProductConfigurableOptions({
product,
configuration: context.state.current_configuration,
setConfigurableProductOptions: config.cart.setConfigurableProductOptions
}) // set the custom options
}
return selectedVariant
}

export function configureProductAsync (context, { product, configuration, selectDefaultVariant = true, fallbackToDefaultWhenNoAvailable = true, setProductErorrs = false }) {
Logger.warn('deprecated, will be not used from 1.12')()
// use current product if product wasn't passed
if (product === null) product = context.getters.getCurrentProduct
const hasConfigurableChildren = (product.configurable_children && product.configurable_children.length > 0)

if (hasConfigurableChildren) {
// handle custom_attributes for easier comparing in the future
product.configurable_children.forEach((child) => {
let customAttributesAsObject = {}
if (child.custom_attributes) {
child.custom_attributes.forEach((attr) => {
customAttributesAsObject[attr.attribute_code] = attr.value
})
// add values from custom_attributes in a different form
Object.assign(child, customAttributesAsObject)
}
})
// find selected variant
let desiredProductFound = false
let selectedVariant = findConfigurableVariant({ product, configuration, availabilityCheck: true })
if (!selectedVariant) {
if (fallbackToDefaultWhenNoAvailable) {
selectedVariant = findConfigurableVariant({ product, selectDefaultChildren: true, availabilityCheck: true }) // return first available child
desiredProductFound = false
} else {
desiredProductFound = false
}
} else {
desiredProductFound = true
}

if (selectedVariant) {
if (!desiredProductFound && selectDefaultVariant /** don't change the state when no selectDefaultVariant is set */) { // update the configuration
populateProductConfigurationAsync(context, { product: product, selectedVariant: selectedVariant })
configuration = context.state.current_configuration
}
if (setProductErorrs) {
product.errors = {} // clear the product errors
}
product.is_configured = true

if (config.cart.setConfigurableProductOptions && !selectDefaultVariant && !(Object.keys(configuration).length === 1 && configuration.sku)) {
// the condition above: if selectDefaultVariant - then "setCurrent" is seeting the configurable options; if configuration = { sku: '' } -> this is a special case when not configuring the product but just searching by sku
setProductConfigurableOptions({
product,
configuration,
setConfigurableProductOptions: config.cart.setConfigurableProductOptions
}) // set the custom options
}/* else {
Logger.debug('Skipping configurable options setup', configuration)()
} */
const fieldsToOmit = ['name']
if (!hasImage(selectedVariant)) fieldsToOmit.push('image')
selectedVariant = omit(selectedVariant, fieldsToOmit) // We need to send the parent SKU to the Magento cart sync but use the child SKU internally in this case
// use chosen variant for the current product
if (selectDefaultVariant) {
context.dispatch('setCurrent', selectedVariant)
}
EventBus.$emit('product-after-configure', { product: product, configuration: configuration, selectedVariant: selectedVariant })
}
if (!selectedVariant && setProductErorrs) { // can not find variant anyway, even the default one
product.errors.variants = i18n.t('No available product variants')
if (selectDefaultVariant) {
context.dispatch('setCurrent', product) // without the configuration
}
}
return selectedVariant
} else {
if (fallbackToDefaultWhenNoAvailable) {
return product
} else {
return null
}
}
}
Loading