diff --git a/src/app/core/models/category-tree/category-tree.helper.ts b/src/app/core/models/category-tree/category-tree.helper.ts index 78b387766d..5fd53cc8b1 100644 --- a/src/app/core/models/category-tree/category-tree.helper.ts +++ b/src/app/core/models/category-tree/category-tree.helper.ts @@ -52,10 +52,6 @@ export class CategoryTreeHelper { }; } - private static removeDuplicates(input: T[]): T[] { - return input.filter((value, index, array) => array.indexOf(value) === index); - } - /** * Select {@link Category} for update */ @@ -66,63 +62,17 @@ export class CategoryTreeHelper { return current; } - private static mergeEdges( - current: { [id: string]: string[] }, - incoming: { [id: string]: string[] } - ): { [id: string]: string[] } { - const edges = { ...current }; - Object.keys(incoming).forEach(key => { - if (current[key]) { - let master: string[]; - let slave: string[]; - - // node with more available edges is trustworthy - if (incoming[key] && incoming[key].length > current[key].length) { - master = incoming[key]; - slave = current[key]; - } else { - master = current[key]; - slave = incoming[key]; - } - - // add edges from both and remove duplicates - edges[key] = CategoryTreeHelper.removeDuplicates([...master, ...slave]); - } else { - edges[key] = [...incoming[key]]; - } - }); - return edges; - } - - private static mergeRootIDs(current: string[], incoming: string[]): string[] { - // node with more available rootIDs is trustworthy - if (incoming && incoming.length > current.length) { - return CategoryTreeHelper.removeDuplicates([...incoming, ...current]); - } else { - return CategoryTreeHelper.removeDuplicates([...current, ...incoming]); - } - } - - private static mergeNodes( - current: { [id: string]: Category }, - incoming: { [id: string]: Category } - ): { [id: string]: Category } { - const nodes = { ...current }; - Object.keys(incoming).forEach(key => { - nodes[key] = { ...CategoryTreeHelper.updateStrategy(current[key], incoming[key]) }; - }); - return nodes; - } - - private static mergeCategoryRefs( - current: { [id: string]: string }, - incoming: { [id: string]: Category } - ): { [id: string]: string } { - const refs = { ...current }; - Object.keys(incoming).forEach(key => { - refs[incoming[key]?.categoryRef] = key; - }); - return refs; + /** + * Perform check for equality. Order of items is ignored. + */ + static equals(tree1: CategoryTree, tree2: CategoryTree): boolean { + return ( + tree1 && + tree2 && + CategoryTreeHelper.rootIdsEqual(tree1.rootIds, tree2.rootIds) && + CategoryTreeHelper.edgesEqual(tree1.edges, tree2.edges) && + CategoryTreeHelper.categoriesEqual(tree1.nodes, tree2.nodes) + ); } /** @@ -167,6 +117,65 @@ export class CategoryTreeHelper { }; } + private static mergeRootIDs(current: string[], incoming: string[]): string[] { + // node with more available rootIDs is trustworthy + if (incoming && incoming.length > current.length) { + return CategoryTreeHelper.removeDuplicates([...incoming, ...current]); + } else { + return CategoryTreeHelper.removeDuplicates([...current, ...incoming]); + } + } + + private static mergeNodes( + current: { [id: string]: Category }, + incoming: { [id: string]: Category } + ): { [id: string]: Category } { + const nodes = { ...current }; + Object.keys(incoming).forEach(key => { + nodes[key] = { ...CategoryTreeHelper.updateStrategy(current[key], incoming[key]) }; + }); + return nodes; + } + + private static mergeCategoryRefs( + current: { [id: string]: string }, + incoming: { [id: string]: Category } + ): { [id: string]: string } { + const refs = { ...current }; + Object.keys(incoming).forEach(key => { + refs[incoming[key]?.categoryRef] = key; + }); + return refs; + } + + private static mergeEdges( + current: { [id: string]: string[] }, + incoming: { [id: string]: string[] } + ): { [id: string]: string[] } { + const edges = { ...current }; + Object.keys(incoming).forEach(key => { + if (current[key]) { + let master: string[]; + let slave: string[]; + + // node with more available edges is trustworthy + if (incoming[key] && incoming[key].length > current[key].length) { + master = incoming[key]; + slave = current[key]; + } else { + master = current[key]; + slave = incoming[key]; + } + + // add edges from both and remove duplicates + edges[key] = CategoryTreeHelper.removeDuplicates([...master, ...slave]); + } else { + edges[key] = [...incoming[key]]; + } + }); + return edges; + } + private static rootIdsEqual(t1: string[], t2: string[]) { return t1.length === t2.length && t1.every(e => t2.includes(e)); } @@ -185,16 +194,7 @@ export class CategoryTreeHelper { ); } - /** - * Perform check for equality. Order of items is ignored. - */ - static equals(tree1: CategoryTree, tree2: CategoryTree): boolean { - return ( - tree1 && - tree2 && - CategoryTreeHelper.rootIdsEqual(tree1.rootIds, tree2.rootIds) && - CategoryTreeHelper.edgesEqual(tree1.edges, tree2.edges) && - CategoryTreeHelper.categoriesEqual(tree1.nodes, tree2.nodes) - ); + private static removeDuplicates(input: T[]): T[] { + return input.filter((value, index, array) => array.indexOf(value) === index); } } diff --git a/src/app/core/models/content-page-tree/content-page-tree.helper.ts b/src/app/core/models/content-page-tree/content-page-tree.helper.ts index 92d359f1b0..6851bf7d40 100644 --- a/src/app/core/models/content-page-tree/content-page-tree.helper.ts +++ b/src/app/core/models/content-page-tree/content-page-tree.helper.ts @@ -44,6 +44,30 @@ export class ContentPageTreeHelper { }; } + /** + * Merge two trees to a new tree. + * Updates nodes according to updateStrategy. + */ + static merge(current: ContentPageTree, incoming: ContentPageTree): ContentPageTree { + if (!current || !incoming) { + throw new Error('falsy input'); + } + + return { + edges: ContentPageTreeHelper.mergeEdges(current.edges, incoming.edges), + nodes: ContentPageTreeHelper.mergeNodes(current.nodes, incoming.nodes), + rootIds: ContentPageTreeHelper.mergeRootIDs(current.rootIds, incoming.rootIds), + }; + } + + /** + * Helper method for adding a single element to a tree. + */ + static add(tree: ContentPageTree, element: ContentPageTreeElement): ContentPageTree { + const singleContentPageTree = ContentPageTreeHelper.single(element); + return ContentPageTreeHelper.merge(tree, singleContentPageTree); + } + private static removeDuplicates(input: T[]): T[] { return input.filter((value, index, array) => array.indexOf(value) === index); } @@ -95,28 +119,4 @@ export class ContentPageTreeHelper { return ContentPageTreeHelper.removeDuplicates([...current, ...incoming]); } } - - /** - * Merge two trees to a new tree. - * Updates nodes according to updateStrategy. - */ - static merge(current: ContentPageTree, incoming: ContentPageTree): ContentPageTree { - if (!current || !incoming) { - throw new Error('falsy input'); - } - - return { - edges: ContentPageTreeHelper.mergeEdges(current.edges, incoming.edges), - nodes: ContentPageTreeHelper.mergeNodes(current.nodes, incoming.nodes), - rootIds: ContentPageTreeHelper.mergeRootIDs(current.rootIds, incoming.rootIds), - }; - } - - /** - * Helper method for adding a single element to a tree. - */ - static add(tree: ContentPageTree, element: ContentPageTreeElement): ContentPageTree { - const singleContentPageTree = ContentPageTreeHelper.single(element); - return ContentPageTreeHelper.merge(tree, singleContentPageTree); - } } diff --git a/src/app/core/models/price/price.helper.ts b/src/app/core/models/price/price.helper.ts index 51c46a3c42..1558a33483 100644 --- a/src/app/core/models/price/price.helper.ts +++ b/src/app/core/models/price/price.helper.ts @@ -3,21 +3,6 @@ import { PriceItem } from 'ish-core/models/price-item/price-item.model'; import { Price } from './price.model'; export class PriceHelper { - private static sanityChecks(p1: Price, p2: Price) { - if (!p1 || !p2) { - throw new Error('cannot handle undefined inputs'); - } - if (!Number.isFinite(p1.value) || !Number.isFinite(p2.value)) { - throw new Error('cannot handle undefined values'); - } - if (!p1.currency || !p2.currency) { - throw new Error('cannot handle undefined currency'); - } - if (p1.currency !== p2.currency) { - throw new Error('currency mismatch'); - } - } - static diff(p1: Price, p2: Price): Price { PriceHelper.sanityChecks(p1, p2); return { @@ -77,4 +62,19 @@ export class PriceHelper { } return { currency, value, type: 'Money' }; } + + private static sanityChecks(p1: Price, p2: Price) { + if (!p1 || !p2) { + throw new Error('cannot handle undefined inputs'); + } + if (!Number.isFinite(p1.value) || !Number.isFinite(p2.value)) { + throw new Error('cannot handle undefined values'); + } + if (!p1.currency || !p2.currency) { + throw new Error('cannot handle undefined currency'); + } + if (p1.currency !== p2.currency) { + throw new Error('currency mismatch'); + } + } } diff --git a/src/app/core/models/product-variation/product-variation.helper.ts b/src/app/core/models/product-variation/product-variation.helper.ts index c0941b3d1d..7cf9a9ed95 100644 --- a/src/app/core/models/product-variation/product-variation.helper.ts +++ b/src/app/core/models/product-variation/product-variation.helper.ts @@ -9,55 +9,6 @@ import { VariationOptionGroup } from './variation-option-group.model'; import { VariationSelectOption } from './variation-select-option.model'; export class ProductVariationHelper { - /** - * Check specific option if perfect variant match is not existing. - * @param option The select option to check. - * @returns Indicates if no perfect match is found. - * TODO: Refactor this to a more functional style - */ - private static alternativeCombinationCheck(option: VariationSelectOption, product: ProductView): boolean { - if (!product.variableVariationAttributes?.length) { - return; - } - - let quality: number; - const perfectMatchQuality = product.variableVariationAttributes.length; - - // loop all selected product attributes ignoring the ones related to currently checked option. - for (const selectedAttribute of product.variableVariationAttributes) { - // loop all possible variations - for (const variation of product.variations) { - quality = 0; - - // loop attributes of possible variation. - for (const attribute of variation.variableVariationAttributes || []) { - // increment quality if variation attribute matches selected product attribute. - if ( - attribute.variationAttributeId === selectedAttribute.variationAttributeId && - attribute.value === selectedAttribute.value - ) { - quality += 1; - continue; - } - - // increment quality if variation attribute matches currently checked option. - if (attribute.variationAttributeId === option.type && attribute.value === option.value) { - quality += 1; - continue; - } - } - - // perfect match found - if (quality === perfectMatchQuality) { - return false; - } - } - } - - // imperfect match - return true; - } - /** * Build select value structure */ @@ -104,23 +55,6 @@ export class ProductVariationHelper { }); } - private static simplifyVariableVariationAttributes(attrs: VariationAttribute[]): { [name: string]: string } { - return attrs - .map(attr => ({ - name: attr.variationAttributeId, - value: attr.value, - })) - .reduce((acc, val) => ({ ...acc, [val.name]: val.value }), {}); - } - - private static difference(obj1: { [name: string]: string }, obj2: { [name: string]: string }): number { - const keys = Object.keys(obj1); - if (keys.length !== Object.keys(obj2).length || keys.some(k => Object.keys(obj2).indexOf(k) < 0)) { - throw new Error("cannot calculate difference if objects don't have the same keys"); - } - return keys.reduce((sum, key) => (obj1[key] !== obj2[key] ? sum + 1 : sum), 0); - } - static findPossibleVariation(name: string, value: string, product: ProductView): string { const target = omit( ProductVariationHelper.simplifyVariableVariationAttributes(product.variableVariationAttributes), @@ -177,4 +111,71 @@ export class ProductVariationHelper { ) ).length; } + + /** + * Check specific option if perfect variant match is not existing. + * + * @param option The select option to check. + * @returns Indicates if no perfect match is found. + * TODO: Refactor this to a more functional style + */ + private static alternativeCombinationCheck(option: VariationSelectOption, product: ProductView): boolean { + if (!product.variableVariationAttributes?.length) { + return; + } + + let quality: number; + const perfectMatchQuality = product.variableVariationAttributes.length; + + // loop all selected product attributes ignoring the ones related to currently checked option. + for (const selectedAttribute of product.variableVariationAttributes) { + // loop all possible variations + for (const variation of product.variations) { + quality = 0; + + // loop attributes of possible variation. + for (const attribute of variation.variableVariationAttributes || []) { + // increment quality if variation attribute matches selected product attribute. + if ( + attribute.variationAttributeId === selectedAttribute.variationAttributeId && + attribute.value === selectedAttribute.value + ) { + quality += 1; + continue; + } + + // increment quality if variation attribute matches currently checked option. + if (attribute.variationAttributeId === option.type && attribute.value === option.value) { + quality += 1; + continue; + } + } + + // perfect match found + if (quality === perfectMatchQuality) { + return false; + } + } + } + + // imperfect match + return true; + } + + private static simplifyVariableVariationAttributes(attrs: VariationAttribute[]): { [name: string]: string } { + return attrs + .map(attr => ({ + name: attr.variationAttributeId, + value: attr.value, + })) + .reduce((acc, val) => ({ ...acc, [val.name]: val.value }), {}); + } + + private static difference(obj1: { [name: string]: string }, obj2: { [name: string]: string }): number { + const keys = Object.keys(obj1); + if (keys.length !== Object.keys(obj2).length || keys.some(k => Object.keys(obj2).indexOf(k) < 0)) { + throw new Error("cannot calculate difference if objects don't have the same keys"); + } + return keys.reduce((sum, key) => (obj1[key] !== obj2[key] ? sum + 1 : sum), 0); + } } diff --git a/src/app/core/models/server-config/server-config.mapper.ts b/src/app/core/models/server-config/server-config.mapper.ts index a59bd088a4..24ae13e0a5 100644 --- a/src/app/core/models/server-config/server-config.mapper.ts +++ b/src/app/core/models/server-config/server-config.mapper.ts @@ -4,6 +4,13 @@ import { ServerConfigData, ServerConfigDataEntry } from './server-config.interfa import { ServerConfig } from './server-config.model'; export class ServerConfigMapper { + static fromData(payload: ServerConfigData): ServerConfig { + if (payload && payload.data) { + return ServerConfigMapper.mapEntries(payload.data); + } + return {}; + } + private static transformType(val: unknown) { if (typeof val === 'string') { if (!isNaN(+val)) { @@ -35,11 +42,4 @@ export class ServerConfigMapper { {} ); } - - static fromData(payload: ServerConfigData): ServerConfig { - if (payload && payload.data) { - return ServerConfigMapper.mapEntries(payload.data); - } - return {}; - } } diff --git a/src/app/extensions/quoting/models/quoting/quoting.helper.ts b/src/app/extensions/quoting/models/quoting/quoting.helper.ts index ea8cab8789..acf648b23a 100644 --- a/src/app/extensions/quoting/models/quoting/quoting.helper.ts +++ b/src/app/extensions/quoting/models/quoting/quoting.helper.ts @@ -9,10 +9,6 @@ export class QuotingHelper { return !QuotingHelper.isStub(entity); } - private static isQuote(entity: QuotingEntity): entity is Quote { - return QuotingHelper.isNotStub(entity) && entity.type === 'Quote'; - } - /** * sorts entity stubs to the bottom and all other quotes and quote requests descending by creationDate */ @@ -47,4 +43,8 @@ export class QuotingHelper { static asQuoteRequest(entity: QuotingEntity): QuoteRequest { return entity as QuoteRequest; } + + private static isQuote(entity: QuotingEntity): entity is Quote { + return QuotingHelper.isNotStub(entity) && entity.type === 'Quote'; + } }